Автор оригинала: Pankaj Kumar.
Java HashSet-самая популярная реализация интерфейса Set. java.util.Хэш-набор поддерживается хэш-картой. HashSet расширяет класс AbstractSet и реализует набор, клонируемые и сериализуемые интерфейсы.
Набор хэшей Java
Некоторые из важных моментов, связанных с набором хэшей в java, следующие;
- Хэш-набор не допускает дублирования записей.
- Хэш-набор допускает значение null в качестве значения.
- HashSet не гарантирует порядок вставки элементов.
- Набор хэшей не является потокобезопасным. Вы можете получить потокобезопасный хэш-набор, используя
Коллекции.Метод synchronizedSet
за счет производительности. Вы также можете использоватьCopyOnWriteArraySet
класс параллелизма для обеспечения потокобезопасности. - Методы итератора хэш-наборов работают быстро. Таким образом, любая структурная модификация набора после создания итератора вызовет исключение ConcurrentModificationException.
- HashSet поддерживает универсальные, это рекомендуемый подход, позволяющий избежать исключения ClassCastException во время выполнения.
- HashSet использует HashMap для хранения элементов, поэтому объекты должны обеспечивать хорошую реализацию метода hashCode() и equals (), чтобы избежать нежелательных результатов.
Конструкторы хэш-наборов Java
Java HashSet предоставляет четыре конструктора.
- общедоступный хэш-набор() : Создает новый пустой хэш-набор, резервная хэш-карта инициализируется с начальной емкостью по умолчанию 16 и коэффициентом загрузки 0,75.
- общедоступный хэш-набор(int initialCapacity) : Создает пустой хэш-набор с резервной хэш-картой, инициализированной с заданной емкостью и коэффициентом загрузки 0,75.
- общедоступный хэш-набор(int initialCapacity, коэффициент загрузки с плавающей точкой) : Создает пустой хэш-набор с резервной хэш-картой, инициализированной с заданной емкостью и заданным коэффициентом загрузки.
- общедоступный хэш-набор(Коллекция расширяет E> c) : Создает новый набор, содержащий элементы указанной коллекции. Резервная хэш-карта создается с коэффициентом загрузки по умолчанию (0,75) и начальной емкостью, достаточной для размещения всех элементов в указанной коллекции. расширяет E> c)
Ниже приведен фрагмент кода, показывающий пример использования всех этих конструкторов хэш-наборов.
Setset = new HashSet<>(); //initial capacity should be power of 2 set = new HashSet<>(32); //setting backing HashMap initial capacity and load factor set = new HashSet<>(32, 0.80f); //creating HashSet from another Collection Set set1 = new HashSet<>(set); Set set2 = new HashSet<>(new ArrayList<>());
Методы набора хэшей Java
Некоторые из полезных методов набора хэшей являются;
- публичное логическое добавление(E e) : Добавляет данный элемент в набор, если он еще не присутствует. Этот метод внутренне использует метод equals() для проверки наличия дубликатов, поэтому убедитесь, что ваш объект правильно определяет метод equals ().
- public void clear() : Удаляет все элементы из набора.
- клон общедоступного объекта() : Возвращает неглубокую копию экземпляра набора.
- общедоступное логическое значение содержит(объект o) : Возвращает значение true, если набор содержит данный элемент, в противном случае значение false.
- public boolean isEmpty() : Возвращает значение true, если набор не содержит элементов, в противном случае значение false.
- общедоступный итератор итератор() : Возвращает итератор по элементам в этом наборе. Элементы возвращаются без определенного порядка.
- публичное логическое удаление(объект o) : Удаляет данный элемент из этого набора, если он присутствует, и возвращает значение true. Если элемент отсутствует в наборе, просто возвращает значение false.
- public int size() : Возвращает количество элементов в наборе.
- общедоступный разделитель разделитель() : Создает разделитель с поздней привязкой и быстрым завершением работы над элементами в этом наборе. Это введено в Java 8, однако я не использовал его до сих пор.
- public boolean removeAll(коллекция c) : Хэш-набор наследует этот метод от абстрактного набора. Этот метод удалит все элементы в наборе, которые являются частью указанной коллекции.
Пример набора хэшей Java
Пример программы Java HashSet, показывающей общее использование HashSet в java.
package com.journaldev.examples; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; public class HashSetExample { public static void main(String[] args) { Setfruits = new HashSet<>(); //add example fruits.add("Apple"); fruits.add("Banana"); //isEmpty example System.out.println("fruits set is empty = "+fruits.isEmpty()); //contains example System.out.println("fruits contains Apple = "+fruits.contains("Apple")); System.out.println("fruits contains Mango = "+fruits.contains("Mango")); //remove example System.out.println("Apple removed from fruits set = "+fruits.remove("Apple")); System.out.println("Mango removed from fruits set = "+fruits.remove("Mango")); //size example System.out.println("fruits set size = "+fruits.size()); //addAll example List list = new ArrayList<>(); list.add("Apple"); list.add("Apple"); list.add("Banana"); list.add("Mango"); System.out.println("fruits set before addAll = "+fruits); System.out.println("list = "+list); fruits.addAll(list); System.out.println("fruits set after addAll = "+fruits); //iterator example Iterator iterator = fruits.iterator(); while(iterator.hasNext()){ System.out.println("Consuming fruit "+iterator.next()); } //removeAll example fruits.add("Orange"); System.out.println("fruits set before removeAll = "+fruits); System.out.println("list = "+list); fruits.removeAll(list); System.out.println("fruits set after removeAll = "+fruits); //clear example fruits.clear(); System.out.println("fruits set is empty = "+fruits.isEmpty()); } }
Вывод приведенного выше примера программы HashSet приведен ниже, я не объясняю их, так как они понятны сами по себе.
fruits set is empty = false fruits contains Apple = true fruits contains Mango = false Apple removed from fruits set = true Mango removed from fruits set = false fruits set size = 1 fruits set before addAll = [Banana] list = [Apple, Apple, Banana, Mango] fruits set after addAll = [Apple, Mango, Banana] Consuming fruit Apple Consuming fruit Mango Consuming fruit Banana fruits set before removeAll = [Apple, Mango, Orange, Banana] list = [Apple, Apple, Banana, Mango] fruits set after removeAll = [Orange] fruits set is empty = true
Пример исключения ConcurrentModificationException набора хэшей Java
Итератор Java HashSet работает быстро, поэтому его методы будут выдавать java.util.Исключение ConcurrentModificationException
если набор структурно изменен. Ниже приведен простой пример, демонстрирующий это.
package com.journaldev.examples; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample { public static void main(String[] args) { Setfruits = new HashSet<>(); //add example fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator iterator = fruits.iterator(); while(iterator.hasNext()){ String fruit = iterator.next(); System.out.println("Processing "+fruit); //wrong way of removing from Set, can throw java.util.ConcurrentModificationException if("Orange".equals(fruit)) fruits.remove("Orange"); } } }
Я получаю результат ниже и исключение, когда выполняется вышеуказанная программа.
Processing Apple Processing Mango Processing Orange Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) at java.util.HashMap$KeyIterator.next(HashMap.java:1453) at com.journaldev.examples.HashSetConcurrentModificationExceptionExample.main(HashSetConcurrentModificationExceptionExample.java:21)
Обратите внимание, что не гарантируется, что элементы хэш-набора будут упорядочены, и Исключение ConcurrentModificationException вызывается iterator.next()
вызовом. Поэтому, если “Оранжевый” является последним в итераторе, вы не получите исключение, потому что iterator.hasNext()
вернет false и iterator.next()
не будет вызван.
Мы всегда должны использовать итерационные методы для структурной модификации, как показано в приведенном ниже примере кода.
package com.journaldev.examples; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample { public static void main(String[] args) { Setfruits = new HashSet<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator iterator = fruits.iterator(); while(iterator.hasNext()){ String fruit = iterator.next(); System.out.println("Processing "+fruit); //correct way of structural modification of Set if("Orange".equals(fruit)) iterator.remove(); } System.out.println("fruits set after iteration = "+fruits); } }
Приведенный выше пример итератора хэш-набора не вызовет исключения, и вы получите результат ниже.
Processing Apple Processing Mango Processing Orange Processing Banana fruits set after iteration = [Apple, Mango, Banana]
Пример хэш-набора Java для массива
Иногда нам приходится преобразовывать хэш-набор в массив и наоборот. Ниже приведена простая программа, показывающая правильный способ преобразования хэш-набора в массив, а затем массива в хэш-набор.
package com.journaldev.examples; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class HashSetToArrayExample { public static void main(String[] args) { Setints = new HashSet<>(); for(int i=0; i<10; i++){ ints.add(i); } System.out.println("ints set = "+ints); // set to array example Integer[] intArray = new Integer[ints.size()]; intArray = ints.toArray(intArray); System.out.println("intArray = "+Arrays.toString(intArray)); ints.remove(0);ints.remove(1); System.out.println("intArray = "+Arrays.toString(intArray)); //array to set example ints = new HashSet<>(Arrays.asList(intArray)); System.out.println("ints from array = "+ints); ints.remove(0);ints.remove(1); System.out.println("ints from array after remove = "+ints); System.out.println("intArray = "+Arrays.toString(intArray)); } }
Вывод приведенного выше хэш-набора в пример массива является;
ints set = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array after remove = [2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Пример хэш-набора Java для списка
Нет большой разницы между Набором и Списком , но иногда нам приходится преобразовывать набор в Список или Список в Набор. Ниже приведен простой пример, показывающий правильный способ преобразования набора в список, а затем списка в набор на java.
package com.journaldev.examples; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class HashSetToListExample { public static void main(String[] args) { Setvowels = new HashSet<>(); vowels.add("a"); vowels.add("e"); vowels.add("i"); //set to list example List vowelsList = new ArrayList<>(vowels); System.out.println("vowels set = "+vowels); System.out.println("vowelsList = "+vowelsList); vowels.add("o"); vowelsList.add("a");vowelsList.add("u"); System.out.println("vowels set = "+vowels); System.out.println("vowelsList = "+vowelsList); //list to set example vowels = new HashSet<>(vowelsList); System.out.println("vowels set = "+vowels); } }
Вывод приведенной выше java-программы, установленной в качестве примера списка, является;
vowels set = [a, e, i] vowelsList = [a, e, i] vowels set = [a, e, i, o] vowelsList = [a, e, i, a, u] vowels set = [a, e, u, i]
Методы Java HashSet equals() и hashCode()
HashSet использует HashMap для хранения своих элементов. HashSet работает с методами equals() и hashCode() для проверки наличия дубликатов элементов при попытке добавить элемент. Давайте посмотрим, что произойдет, если вы установите объект, не обеспечивающий реализацию метода equals ().
package com.journaldev.examples; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance { public static void main(String[] args) { Setemps = new HashSet<>(); emps.add(new Emp(1,"Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int i, String n) { this.id = i; this.name = n; } @Override public String toString(){ return "{"+id+","+name+"}"; } }
Когда мы запускаем вышеописанную программу, мы получаем результат ниже для элементов набора.
[{2,David}, {1,Pankaj}, {1,Pankaj}]
Таким образом, похоже, что мы смогли сохранить дубликаты элементов в наборе. На самом деле нет, это происходит потому, что класс Emp не определяет метод equals (), поэтому используется реализация метода класса объектов equals (). Класс объектов определяет метод equals (), как показано ниже.
public boolean equals(Object obj) { return (this == obj); }
Поэтому при добавлении нового элемента проверяется ссылка на объект, а не содержимое. Следовательно, у нас есть объекты с дублированным содержимым, однако у них разные ссылки. Давайте посмотрим, что произойдет, когда мы определим методы hashCode() и equals() в классе Emp.
package com.journaldev.examples; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance { public static void main(String[] args) { Setemps = new HashSet<>(); emps.add(new Emp(1,"Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); Emp e = new Emp(3, "Lisa"); emps.add(e); System.out.println(emps); //set values to make it duplicate e.setId(1); System.out.println(emps); e.setName("Pankaj"); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int i, String n) { this.setId(i); this.setName(n); } @Override public boolean equals(Object obj){ if(obj == null || !(obj instanceof Emp)) return false; Emp e = (Emp) obj; if(e.getId() == this.getId() && this.getName().equals(e.getName())) return true; return false; } @Override public int hashCode(){ return getId(); } @Override public String toString(){ return "{"+getId()+","+getName()+"}"; } public String getName() { return name; } public int getId() { return id; } public void setName(String name) { this.name = name; } public void setId(int id) { this.id = id; } }
На этот раз наша программа выдает следующий результат.
[{1,Pankaj}, {2,David}] [{1,Pankaj}, {2,David}, {3,Lisa}] [{1,Pankaj}, {2,David}, {1,Lisa}] [{1,Pankaj}, {2,David}, {1,Pankaj}]
Обратите внимание, что HashSet смог проверить наличие дубликатов, когда мы попытались добавить элемент. Но мы можем изменить значения объекта с помощью методов настройки и сделать его дубликатом. Это сработало, потому что на съемочной площадке не выполняется никаких операций. Вот почему неизменяемые объекты лучше работают с набором и картой.
Это все для примера учебника по хеш-набору Java, я надеюсь, что все важные вещи описаны для хеш-набора в Java. Если я что-то пропустил, пожалуйста, дайте мне знать в комментариях, и я тоже постараюсь это добавить.