HashSet
HashSet, Java Collections Framework-ün bir hissəsi olan və unikal elementləri saxlayan bir data strukturudur. Set interface-ini implement edir və daxili olaraq HashMap istifadə edir. HashSet-in əsas xüsusiyyəti, təkrarlanan elementlərə icazə verməməsi və elementləri hash code-larına əsasən saxlamasıdır.
HashSet-in Əsas Xüsusiyyətləri
- Unikal Elementlər: Təkrarlanan elementlərə icazə vermir
- Sırasız: Elementləri daxiletmə sırasına görə saxlamır
- Null Dəstəyi: Bir ədəd null element saxlaya bilər
- Sürətli Əməliyyatlar: Ortalama O(1) zamanda axtarış, əlavə etmə və silmə əməliyyatları
- Daxili Struktur: Daxili olaraq HashMap istifadə edir
HashSet-in İşləmə Prinsipi
HashSet, daxili olaraq HashMap istifadə edir. Hər bir element HashMap-də key kimi saxlanılır və value olaraq sabit bir dummy object istifadə olunur. Bu səbəbdən, HashSet-in performansı və davranışı HashMap-ə çox bənzəyir.
HashSet-in Java-da İmplementasiyası
Java-da HashSet class-ı java.util paketində yerləşir:
Koda bax
import java.util.HashSet;
import java.util.Iterator;
public class HashSetExample {
public static void main(String[] args) {
// HashSet yaratmaq
HashSet<String> set = new HashSet<>();
// Elementləri əlavə etmək (add)
set.add("Alma");
set.add("Armud");
set.add("Banan");
set.add("Alma"); // Təkrar element - əlavə olunmayacaq
System.out.println("HashSet: " + set);
// Elementin olub-olmadığını yoxlamaq (contains)
boolean hasElement = set.contains("Banan");
System.out.println("Banan elementi var? " + hasElement);
// Elementi silmək (remove)
set.remove("Armud");
System.out.println("Armud silindikdən sonra: " + set);
// HashSet-in ölçüsü (size)
System.out.println("HashSet-in ölçüsü: " + set.size());
// HashSet boşdur? (isEmpty)
System.out.println("HashSet boşdur? " + set.isEmpty());
// HashSet-i iterate etmək
System.out.println("\nHashSet-i iterate etmək:");
// 1. for-each ilə iterate
for (String element : set) {
System.out.println(element);
}
// 2. Iterator ilə iterate
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 3. forEach ilə iterate (Java 8+)
set.forEach(element -> System.out.println(element));
// HashSet-i təmizləmək (clear)
set.clear();
System.out.println("Clear-dən sonra HashSet: " + set);
}
}
HashSet-in Sadə İmplementasiyası
Aşağıda HashSet-in sadə bir implementasiyası verilmişdir (HashMap istifadə edərək):
Koda bax
import java.util.ArrayList;
import java.util.List;
public class CustomHashSet<T> {
private static final int DEFAULT_CAPACITY = 16;
private static final double LOAD_FACTOR = 0.75;
private List<T>[] buckets;
private int size;
private int capacity;
@SuppressWarnings("unchecked")
public CustomHashSet() {
this.capacity = DEFAULT_CAPACITY;
this.buckets = new ArrayList[capacity];
this.size = 0;
for (int i = 0; i < capacity; i++) {
buckets[i] = new ArrayList<>();
}
}
// Element əlavə et
public boolean add(T element) {
if (element == null) return false;
int index = getIndex(element);
List<T> bucket = buckets[index];
// Artıq varsa əlavə etmə
if (bucket.contains(element)) {
return false;
}
bucket.add(element);
size++;
// Lazım olarsa resize et
if (size > capacity * LOAD_FACTOR) {
resize();
}
return true;
}
// Element sil
public boolean remove(T element) {
if (element == null) return false;
int index = getIndex(element);
List<T> bucket = buckets[index];
if (bucket.remove(element)) {
size--;
return true;
}
return false;
}
// Element var mı yoxla
public boolean contains(T element) {
if (element == null) return false;
int index = getIndex(element);
return buckets[index].contains(element);
}
// Ölçü qaytar
public int size() {
return size;
}
// Boş mu yoxla
public boolean isEmpty() {
return size == 0;
}
// Təmizlə
public void clear() {
for (List<T> bucket : buckets) {
bucket.clear();
}
size = 0;
}
// Bütün elementləri array kimi qaytar
@SuppressWarnings("unchecked")
public T[] toArray() {
T[] result = (T[]) new Object[size];
int index = 0;
for (List<T> bucket : buckets) {
for (T element : bucket) {
result[index++] = element;
}
}
return result;
}
// Hash index hesabla
private int getIndex(T element) {
return Math.abs(element.hashCode()) % capacity;
}
// Capacity artır
@SuppressWarnings("unchecked")
private void resize() {
List<T>[] oldBuckets = buckets;
capacity *= 2;
buckets = new ArrayList[capacity];
size = 0;
for (int i = 0; i < capacity; i++) {
buckets[i] = new ArrayList<>();
}
// Köhnə elementləri yenidən əlavə et
for (List<T> bucket : oldBuckets) {
for (T element : bucket) {
add(element);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
boolean first = true;
for (List<T> bucket : buckets) {
for (T element : bucket) {
if (!first) {
sb.append(", ");
}
sb.append(element);
first = false;
}
}
sb.append("]");
return sb.toString();
}
// Test üçün
public static void main(String[] args) {
CustomHashSet<String> set = new CustomHashSet<>();
// Test əlavə etmə
set.add("bir");
set.add("iki");
set.add("üç");
set.add("bir"); // dublikat
System.out.println("Set: " + set);
System.out.println("Size: " + set.size());
// Test contains
System.out.println("Contains 'iki': " + set.contains("iki"));
System.out.println("Contains 'dörd': " + set.contains("dörd"));
// Test remove
set.remove("iki");
System.out.println("After remove 'iki': " + set);
System.out.println("Size: " + set.size());
}
}
HashSet-in İstifadə Sahələri
- Unikal Elementlər: Təkrarlanan elementləri aradan qaldırmaq üçün
- Membership Testing: Bir elementin kolleksiyada olub-olmadığını sürətlə yoxlamaq üçün
- Set Operations: Union, intersection, difference kimi set əməliyyatları üçün
- Duplicate Removal: Təkrarlanan elementləri aradan qaldırmaq üçün
- Caching: Unikal dəyərləri cache-ləmək üçün
HashSet-in Praktiki İstifadəsi
Təkrarlanan Elementləri Aradan Qaldırmaq
Koda bax
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class RemoveDuplicates {
public static void main(String[] args) {
// Təkrarlanan elementləri olan list
List<Integer> numbersWithDuplicates = new ArrayList<>();
numbersWithDuplicates.add(1);
numbersWithDuplicates.add(2);
numbersWithDuplicates.add(3);
numbersWithDuplicates.add(1);
numbersWithDuplicates.add(2);
numbersWithDuplicates.add(4);
System.out.println("Original List: " + numbersWithDuplicates);
// HashSet istifadə edərək təkrarları aradan qaldırmaq
HashSet<Integer> uniqueNumbers = new HashSet<>(numbersWithDuplicates);
System.out.println("List without duplicates: " + uniqueNumbers);
// Əgər sıranı saxlamaq lazımdırsa, LinkedHashSet istifadə edə bilərik
// LinkedHashSet<Integer> uniqueOrderedNumbers = new LinkedHashSet<>(numbersWithDuplicates);
// System.out.println("Ordered list without duplicates: " + uniqueOrderedNumbers);
}
}
Set Əməliyyatları
Koda bax
import java.util.HashSet;
public class SetOperations {
public static void main(String[] args) {
// İki set yaratmaq
HashSet<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);
HashSet<Integer> set2 = new HashSet<>();
set2.add(3);
set2.add(4);
set2.add(5);
set2.add(6);
System.out.println("Set1: " + set1);
System.out.println("Set2: " + set2);
// Union (Birləşmə) - addAll
HashSet<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("Union: " + union);
// Intersection (Kəsişmə) - retainAll
HashSet<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("Intersection: " + intersection);
// Difference (Fərq) - removeAll
HashSet<Integer> difference1 = new HashSet<>(set1);
difference1.removeAll(set2);
System.out.println("Difference (Set1 - Set2): " + difference1);
HashSet<Integer> difference2 = new HashSet<>(set2);
difference2.removeAll(set1);
System.out.println("Difference (Set2 - Set1): " + difference2);
// Symmetric Difference (Simmetrik Fərq)
HashSet<Integer> symmetricDifference = new HashSet<>(set1);
symmetricDifference.addAll(set2); // Union
HashSet<Integer> temp = new HashSet<>(set1);
temp.retainAll(set2); // Intersection
symmetricDifference.removeAll(temp); // Union - Intersection
System.out.println("Symmetric Difference: " + symmetricDifference);
}
}
HashSet vs TreeSet vs LinkedHashSet
Java-da Set interface-ini implement edən üç əsas class var:
| Xüsusiyyət | HashSet | TreeSet | LinkedHashSet |
|---|---|---|---|
| Sıralama | Sırasız | Təbii sıralama (natural ordering) | Daxiletmə sırası |
| Performance | Ən sürətli (O(1)) | Daha yavaş (O(log n)) | HashSet-dən bir az yavaş |
| Null Element | Bir null element | Null element yoxdur | Bir null element |
| Implementation | HashMap | TreeMap | LinkedHashMap |
| Ordered | Xeyr | Bəli (sorted) | Bəli (insertion order) |
| Navigable | Xeyr | Bəli | Xeyr |
HashSet-in Mürəkkəbliyi
| Əməliyyat | Average Case | Worst Case |
|---|---|---|
| add | O(1) | O(n) |
| remove | O(1) | O(n) |
| contains | O(1) | O(n) |
| size | O(1) | O(1) |
Qeyd: Worst case o zaman baş verir ki, bütün elementlər eyni bucket-ə düşür (kolliziya).
HashSet-in Java 8-də Təkmilləşdirilməsi
Java 8-də HashSet-in performansını artırmaq üçün bəzi təkmilləşdirmələr edilmişdir (daxili olaraq istifadə etdiyi HashMap-in təkmilləşdirilməsi sayəsində):
- Balanced Tree: Bir bucket-də 8-dən çox element olduqda, linked list əvəzinə balanced tree (Red-Black Tree) istifadə olunur
- Daha Yaxşı Hash Funksiyası: Kolliziyaları azaltmaq üçün daha yaxşı hash funksiyası
HashSet-in Məhdudiyyətləri
- Sırasız: Elementləri daxiletmə sırasına görə saxlamır (əgər sıra lazımdırsa, LinkedHashSet istifadə edin)
- Null Element: Yalnız bir null element saxlaya bilər
- Mutable Objects: Mutable obyektlər HashSet-də key kimi istifadə edildikdə, onların hash code-u dəyişərsə, HashSet-də tapılmaya bilərlər
Nəticə
HashSet, unikal elementləri saxlamaq və onlara sürətli giriş əldə etmək üçün çox faydalı bir data strukturudur. Java-da HashSet class-ı, təkrarlanan elementləri aradan qaldırmaq və set əməliyyatlarını yerinə yetirmək üçün ideal seçimdir. Onun O(1) zamanda əməliyyatları yerinə yetirmək qabiliyyəti, onu bir çox alqoritm və proqram üçün əvəzolunmaz edir.