Рубрики
Без рубрики

Java HashSet – набор хэшей на Java

Хэш-набор Java. Набор хэшей на Java. java.util.Пример хэш-набора добавить, очистить, содержит, размер, пусто, удалить, итератор, удалить все, равно, хэш-код, Список, Массив.

Автор оригинала: Pankaj Kumar.

Java HashSet-самая популярная реализация интерфейса Set. java.util.Хэш-набор поддерживается хэш-картой. HashSet расширяет класс AbstractSet и реализует набор, клонируемые и сериализуемые интерфейсы.

Набор хэшей Java

Некоторые из важных моментов, связанных с набором хэшей в java, следующие;

  1. Хэш-набор не допускает дублирования записей.
  2. Хэш-набор допускает значение null в качестве значения.
  3. HashSet не гарантирует порядок вставки элементов.
  4. Набор хэшей не является потокобезопасным. Вы можете получить потокобезопасный хэш-набор, используя Коллекции.Метод synchronizedSet за счет производительности. Вы также можете использовать CopyOnWriteArraySet класс параллелизма для обеспечения потокобезопасности.
  5. Методы итератора хэш-наборов работают быстро. Таким образом, любая структурная модификация набора после создания итератора вызовет исключение ConcurrentModificationException.
  6. HashSet поддерживает универсальные, это рекомендуемый подход, позволяющий избежать исключения ClassCastException во время выполнения.
  7. HashSet использует HashMap для хранения элементов, поэтому объекты должны обеспечивать хорошую реализацию метода hashCode() и equals (), чтобы избежать нежелательных результатов.

Конструкторы хэш-наборов Java

Java HashSet предоставляет четыре конструктора.

  1. общедоступный хэш-набор() : Создает новый пустой хэш-набор, резервная хэш-карта инициализируется с начальной емкостью по умолчанию 16 и коэффициентом загрузки 0,75.
  2. общедоступный хэш-набор(int initialCapacity) : Создает пустой хэш-набор с резервной хэш-картой, инициализированной с заданной емкостью и коэффициентом загрузки 0,75.
  3. общедоступный хэш-набор(int initialCapacity, коэффициент загрузки с плавающей точкой) : Создает пустой хэш-набор с резервной хэш-картой, инициализированной с заданной емкостью и заданным коэффициентом загрузки.
  4. общедоступный хэш-набор(Коллекция расширяет E> c) : Создает новый набор, содержащий элементы указанной коллекции. Резервная хэш-карта создается с коэффициентом загрузки по умолчанию (0,75) и начальной емкостью, достаточной для размещения всех элементов в указанной коллекции. расширяет E> c)

Ниже приведен фрагмент кода, показывающий пример использования всех этих конструкторов хэш-наборов.

Set set = 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

Некоторые из полезных методов набора хэшей являются;

  1. публичное логическое добавление(E e) : Добавляет данный элемент в набор, если он еще не присутствует. Этот метод внутренне использует метод equals() для проверки наличия дубликатов, поэтому убедитесь, что ваш объект правильно определяет метод equals ().
  2. public void clear() : Удаляет все элементы из набора.
  3. клон общедоступного объекта() : Возвращает неглубокую копию экземпляра набора.
  4. общедоступное логическое значение содержит(объект o) : Возвращает значение true, если набор содержит данный элемент, в противном случае значение false.
  5. public boolean isEmpty() : Возвращает значение true, если набор не содержит элементов, в противном случае значение false.
  6. общедоступный итератор итератор() : Возвращает итератор по элементам в этом наборе. Элементы возвращаются без определенного порядка.
  7. публичное логическое удаление(объект o) : Удаляет данный элемент из этого набора, если он присутствует, и возвращает значение true. Если элемент отсутствует в наборе, просто возвращает значение false.
  8. public int size() : Возвращает количество элементов в наборе.
  9. общедоступный разделитель разделитель() : Создает разделитель с поздней привязкой и быстрым завершением работы над элементами в этом наборе. Это введено в Java 8, однако я не использовал его до сих пор.
  10. 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) {

		Set fruits = 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) {
		Set fruits = 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) {
		Set fruits = 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) {
		Set ints = 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) {
		Set vowels = 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) {

		Set emps = 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) {

		Set emps = 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. Если я что-то пропустил, пожалуйста, дайте мне знать в комментариях, и я тоже постараюсь это добавить.