В множествах (Set) каждое значение может храниться только в одном экземпляре. Попытки добавить новый экземпляр эквивалентного объекта блокируются. Множества часто используются для проверки принадлежности, чтобы вы могли легко проверить, принадлежит ли объект заданному множеству. Следовательно, важнейшей операцией Set является операция поиска, поэтому на практике обычно выбирается реализация HashSet, оптимизированная для быстрого поиска.
Set имеет такой же интерфейс, что и Collection. В сущности, Set и является Collection, но обладает несколько иным поведением (кстати, идеальный пример использования наследования и полиморфизма: выражение разных концепций поведения). Пример использованияHashSet с объектами Integer:
</spoiler> В множество включаются десять тысяч случайных чисел от 0 до 29; естественно, числа должны многократно повторяться. Но при этом мы видим, что в результатах каждое число присутствует только в одном экземпляре.
Также обратите внимание на непредсказуемый порядок следования чисел в выводе. Это объясняется тем, что HashSet использует хеширование для ускорения выборки. Порядок, поддерживаемый HashSet, отличается от порядка TreeSet или LinkedHashSet, поскольку каждая реализация упорядочивает элементы по-своему. Если вы хотите, чтобы результат был отсортирован, воспользуйтесь TreeSet вместо HashSet:
//: holding/SortedSetOfInteger.java
import java.util.*;
public class SortedSetOfInteger {
public static void main(String[] args) {
Random rand = new Random(47);
SortedSet<Integer> intset = new TreeSet<Integer>();
</spoiler> Одной из наиболее распространенных операций со множествами является проверка принадлежности методом contains(), но существуют и другие операции, которые напомнят вам диаграммы Венна из школьного курса:
//: holding/SetOperations.java
import java.util.*;
import static net.mindview.util.Print.*;
public class SetOperations {
public static void main(String[] args) {
Set<String> set1 = new HashSet<String>();
Collections.addAll(set1,
"A B C D E F G H I J K L".split(" "));
set1.add("M");
print("H: " + set1.contains("H"));
print("N: " + set1.contains("N"));
Set<String> set2 = new HashSet<String>();
Collections.addAll(set2, "H I J K L".split(" "));
print("set2 in set1: " + set1.containsAll(set2));
set1.remove("H");
print("set1: " + set1);
print("set2 in set1: " + set1.containsAll(set2));
set1.removeAll(set2);
print("set2 removed from set1: " + set1);
Collections.addAll(set1, "X Y Z".split(" "));
print("'X Y Z' added to set1: " + set1);
}
}
<spoiler text="Output:">
H: true
N: false
set2 in set1: true
set1: [D, K, C, B, L, G, I, M, A, F, J, E]
set2 in set1: false
set2 removed from set1: [D, C, B, G, M, A, F, E]
'X Y Z' added to set1: [Z, D, C, B, G, M, A, F, Y, X, E]
</spoiler> Имена методов говорят за себя. Информацию о других методах Set можно найти в документации JDK.
Карта
Возможность отображения одних объектов на другие (ассоциация) чрезвычайно полезна при решении широкого класса задач программирования. В качестве примера рассмотрим программу, анализирующую качество распределения класса JavaRandom. В идеале классRandom должен выдавать абсолютно равномерное распределение чисел, но чтобы убедиться в этом, необходимо сгенерировать большое количество случайных чисел и подсчитать их количество в разных интервалах. Множества упрощают эту задачу: ключом в данном случае является число, сгенерированное при помощи Random, а значением — количество его вхождений:
</spoiler> В main() механизм автоматической упаковки преобразует случайно сгенерированое целое число в ссылку на Integer, которая может использоваться с HashMap (контейнеры не могут использоваться для хранения примитивов).
Метод get() возвращает null, если элемент отсутствует в контейнере (то есть если число было сгенерировано впервые. В противном случае метод get() возвращает значение Integer, связанное с ключом, и последнее увеличивается на 1 (автоматическая упаковка снова упрощает вычисления, но в действительности при этом выполняются преобразования к Integer и обратно).
Следующий пример демонстрирует поиск объектов Pet по строковому описанию String. Он также показывает, как проверить присутствие некоторого ключа или значения в Map методами containsKey() и containsValue():
//: holding/PetMap.java
import typeinfo.pets.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class PetMap {
public static void main(String[] args) {
Map<String,Pet> petMap = new HashMap<String,Pet>();
petMap.put("My Cat", new Cat("Molly"));
petMap.put("My Dog", new Dog("Ginger"));
petMap.put("My Hamster", new Hamster("Bosco"));
print(petMap);
Pet dog = petMap.get("My Dog");
print(dog);
print(petMap.containsKey("My Dog"));
print(petMap.containsValue(dog));
}
}
<spoiler text="Output:">
{My Cat=Cat Molly, My Hamster=Hamster Bosco, My Dog=Dog Ginger}
Dog Ginger
true
true
</spoiler> Map, по аналогии с массивами и Collection, легко расширяются до нескольких измерений; достаточно создать Map со значениями типа Map (причем значениями этих Map могут быть другие контейнеры, и даже другие Map). Контейнеры легко комбинируются друг с другом, что позволяет быстро создавать сложные структуры данных. Например, если нам потребуется сохранить информацию о владельцах сразу нескольких домашних животных, для этого будет достаточно создать контейнер Map<Person,List<Pet>>:
//: holding/MapOfList.java
package holding;
import typeinfo.pets.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class MapOfList {
public static Map<Person, List<? extends Pet>>
petPeople = new HashMap<Person, List<? extends Pet>>();
Arrays.asList(new Rat("Fuzzy"), new Rat("Fizzy")));
petPeople.put(new Person("Isaac"),
Arrays.asList(new Rat("Freckly")));
}
public static void main(String[] args) {
print("People: " + petPeople.keySet());
print("Pets: " + petPeople.values());
for(Person person : petPeople.keySet()) {
print(person + " has:");
for(Pet pet : petPeople.get(person))
print(" " + pet);
}
}
}
<spoiler text="Output:">
People: [Person Luke, Person Marilyn, Person Isaac, Person Dawn, Person Kate]
Pets: [[Rat Fuzzy, Rat Fizzy], [Pug Louie aka Louis Snorkelstein Dupree, Cat Stanford aka Stinky
el Negro, Cat Pinkola], [Rat Freckly], [Cymric Molly, Mutt Spot], [Cat Shackleton, Cat Elsie May,
Dog Margrett]]
Person Luke has:
Rat Fuzzy
Rat Fizzy
Person Marilyn has:
Pug Louie aka Louis Snorkelstein Dupree
Cat Stanford aka Stinky el Negro
Cat Pinkola
Person Isaac has:
Rat Freckly
Person Dawn has:
Cymric Molly
Mutt Spot
Person Kate has:
Cat Shackleton
Cat Elsie May
Dog Margrett
</spoiler> Map может вернуть множество (Set) своих ключей, коллекцию (Collection) значений или множество (Set) всех пар «ключ-значение». Метод keySet() создает множество всех ключей, которое затем используется в синтаксисе foreach для перебора Map.