← Kursa Dön
📄 Text · 12 min

Set: HashSet, TreeSet, LinkedHashSet

Set, tekrarsız (unique) eleman tutan bir koleksiyon. Aynı elemanı iki kere ekleyemezsin. List'ten en büyük farkı bu — Set'te duplicate yok.

Bunu bir oy sandığı gibi düşün. Her kişi sadece bir kez oy verebilir. İkinci kez gelirse oyunu kabul etmezsin. Set tam olarak bu mantıkta çalışır.

Set Interface Temelleri

Set<String> colors = new HashSet<>();
colors.add("Kırmızı");
colors.add("Mavi");
colors.add("Yeşil");
colors.add("Kırmızı");  // Eklenmez! Zaten var.

System.out.println(colors.size()); // 3 — Kırmızı bir kere
System.out.println(colors);        // Sıra GARANTİSİZ: [Mavi, Kırmızı, Yeşil] veya farklı

Set'te index yok. set.get(0) diye bir method yok. Sıralama garantisi de implementasyona bağlı.

Set<Integer> numbers = new HashSet<>();
numbers.add(5);
numbers.add(3);
numbers.add(8);

// Temel operasyonlar
numbers.contains(3);     // true — arama çok hızlı O(1)
numbers.remove(5);       // true — silindi
numbers.size();           // 2
numbers.isEmpty();        // false

// Gezinme
for (int n : numbers) {
    System.out.println(n);
}

// Stream ile
numbers.forEach(System.out::println);

HashSet: En Hızlı Set

HashSet, arka planda HashMap kullanır. Elemanları hash tablosunda saklar. Ekleme, silme, arama — hepsi ortalama O(1).

Set<String> emails = new HashSet<>();
emails.add("ali@test.com");
emails.add("veli@test.com");
emails.add("ali@test.com"); // false döner — duplicate

System.out.println(emails.contains("ali@test.com")); // true — O(1)

HashSet nasıl çalışır:

  1. hashCode() ile elemanın hash değeri hesaplanır

  2. Bu değer bir "bucket" (kova) index'ine dönüştürülür

  3. Aynı bucket'a düşen elemanlar equals() ile karşılaştırılır

  4. Eğer equals() true döndürürse → duplicate, eklenmez

Bu yüzden hashCode() ve equals() Set'in düzgün çalışması için hayati önemde.

hashCode() ve equals() — Set'in Kalbi

Kendi sınıflarını Set'te kullanırsan, hashCode() ve equals() override etmek zorunlu.

public class Student {
    private int id;
    private String name;
    
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
    // equals override etmezsen — referans karşılaştırması yapar
    // Aynı id'li iki Student farklı sayılır!
}

// SORUN
Set<Student> students = new HashSet<>();
students.add(new Student(1, "Ali"));
students.add(new Student(1, "Ali")); // İkisi de eklenir! Çünkü farklı nesneler.
System.out.println(students.size()); // 2 — yanlış!

Çözüm: equals() ve hashCode() override et.

public class Student {
    private int id;
    private String name;
    
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

// ŞİMDİ DOĞRU
Set<Student> students = new HashSet<>();
students.add(new Student(1, "Ali"));
students.add(new Student(1, "Ali")); // Eklenmez! equals() true döner.
System.out.println(students.size()); // 1 — doğru!

⚠️ Kritik Kural: equals() true dönen iki nesnenin hashCode() değerleri aynı olmalı. Aksi halde HashSet düzgün çalışmaz — eleman farklı bucket'a düşer ve duplicate oluşur.

// KONTRAT:
// a.equals(b) == true  →  a.hashCode() == b.hashCode()  (ZORUNLU)
// a.hashCode() == b.hashCode()  →  a.equals(b) ?         (Zorunlu değil — collision olabilir)

TreeSet: Sıralı Set

TreeSet, elemanları doğal sırada (natural ordering) veya verdiğin Comparator'a göre sıralı tutar. Arka planda kırmızı-siyah ağaç (Red-Black Tree) kullanır.

Set<Integer> sorted = new TreeSet<>();
sorted.add(5);
sorted.add(2);
sorted.add(8);
sorted.add(1);
System.out.println(sorted); // [1, 2, 5, 8] — her zaman sıralı

Set<String> names = new TreeSet<>();
names.add("Zeynep");
names.add("Ali");
names.add("Mehmet");
System.out.println(names); // [Ali, Mehmet, Zeynep] — alfabetik

TreeSet performansı:

OperasyonZaman
addO(log n)
removeO(log n)
containsO(log n)

HashSet'ten yavaş (O(1) vs O(log n)) ama sıralama garantisi var.

// Custom sıralama ile TreeSet
Set<String> byLength = new TreeSet<>(Comparator.comparingInt(String::length));
byLength.add("Java");
byLength.add("C");
byLength.add("Python");
byLength.add("Go");
System.out.println(byLength); // [C, Go, Java, Python] — uzunluğa göre

// DİKKAT: Aynı uzunluktaki elemanlar duplicate sayılır!
byLength.add("JS"); // "Go" ile aynı uzunluk — eklenmez!

⚠️ TreeSet Tuzağı: TreeSet, eşitliği compareTo() (veya Comparator) ile belirler, equals() ile değil. Comparator 0 döndürürse eleman duplicate sayılır ve eklenmez.

// TreeSet'e kendi sınıfını koymak — Comparable zorunlu (veya Comparator ver)
public class Student implements Comparable<Student> {
    private String name;
    
    @Override
    public int compareTo(Student other) {
        return this.name.compareTo(other.name);
    }
}

// Comparable implement etmeyen sınıf → ClassCastException
Set<MyClass> set = new TreeSet<>();
set.add(new MyClass()); // ClassCastException!

LinkedHashSet: Ekleme Sırasını Koruyan Set

LinkedHashSet, HashSet gibi çalışır ama ek olarak ekleme sırasını korur. Arka planda hash tablosu + çift yönlü bağlı liste kullanır.

Set<String> linked = new LinkedHashSet<>();
linked.add("Üçüncü");
linked.add("Birinci");
linked.add("İkinci");
linked.add("Birinci"); // Duplicate — eklenmez

System.out.println(linked); // [Üçüncü, Birinci, İkinci] — ekleme sırası korunur

// HashSet ile karşılaştır
Set<String> hash = new HashSet<>(linked);
System.out.println(hash); // Sıra belirsiz — [İkinci, Birinci, Üçüncü] olabilir

LinkedHashSet, HashSet'ten biraz daha fazla bellek kullanır (linked list referansları yüzünden) ama sıralama garantisi verir.

Üçü Arasındaki Fark

ÖzellikHashSetTreeSetLinkedHashSet
SıralamaYokDoğal/CustomEkleme sırası
add/remove/containsO(1)O(log n)O(1)
null eleman1 tane OK❌ (NullPointerException)1 tane OK
Arka planHashMapRed-Black TreeHashMap + LinkedList
BellekEn azOrtaEn fazla

Ne zaman hangisi:

// Sadece unique'lik lazım, sıra önemli değil → HashSet
Set<String> visited = new HashSet<>();

// Sıralı unique elemanlar lazım → TreeSet
Set<Integer> sortedScores = new TreeSet<>();

// Ekleme sırasını korumak lazım → LinkedHashSet
Set<String> insertionOrder = new LinkedHashSet<>();

Küme Operasyonları

Set'in en güçlü yanlarından biri: matematiksel küme operasyonları.

Set<Integer> a = new HashSet<>(Set.of(1, 2, 3, 4, 5));
Set<Integer> b = new HashSet<>(Set.of(4, 5, 6, 7, 8));

// BİRLEŞİM (Union): A ∪ B
Set<Integer> union = new HashSet<>(a);
union.addAll(b);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]

// KESİŞİM (Intersection): A ∩ B
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b);
System.out.println(intersection); // [4, 5]

// FARK (Difference): A - B
Set<Integer> difference = new HashSet<>(a);
difference.removeAll(b);
System.out.println(difference); // [1, 2, 3]

// SİMETRİK FARK: (A ∪ B) - (A ∩ B)
Set<Integer> symmetric = new HashSet<>(a);
symmetric.addAll(b);
Set<Integer> common = new HashSet<>(a);
common.retainAll(b);
symmetric.removeAll(common);
System.out.println(symmetric); // [1, 2, 3, 6, 7, 8]

Set.of() — Immutable Set (Java 9+)

Set<String> immutable = Set.of("A", "B", "C");
// immutable.add("D");  // UnsupportedOperationException!

// DİKKAT: duplicate veremezsin
// Set.of("A", "A");    // IllegalArgumentException!

// null veremezsin
// Set.of(null);         // NullPointerException!

Gerçek Dünya: Duplicate Temizleme

// List'ten duplicate'leri temizlemenin en kolay yolu
List<String> withDuplicates = List.of("Ali", "Veli", "Ali", "Can", "Veli");
List<String> unique = new ArrayList<>(new LinkedHashSet<>(withDuplicates));
System.out.println(unique); // [Ali, Veli, Can] — sıra korundu, duplicate gitti

// Stream ile
List<String> unique2 = withDuplicates.stream()
    .distinct()
    .collect(Collectors.toList());

💡 İpucu: Duplicate temizlerken sırayı korumak istiyorsan LinkedHashSet kullan. Sıra önemli değilse HashSet yeterli.

Özet

  • Set tekrarsız (unique) eleman tutar — add() duplicate'i reddeder

  • HashSet en hızlı (O(1)) ama sıra garantisi yok — hashCode() ve equals() doğru override edilmeli

  • TreeSet elemanları sıralı tutar (O(log n)) — Comparable veya Comparator gerektirir, null kabul etmez

  • LinkedHashSet ekleme sırasını korur, HashSet kadar hızlı, biraz daha fazla bellek kullanır

  • hashCode/equals kontratı kritik: equals() true ise hashCode() aynı olmalı — aksi halde Set bozulur

  • Küme operasyonları (birleşim, kesişim, fark) addAll, retainAll, removeAll ile yapılır