← Kursa Dön
📄 Text · 15 min

Map: HashMap, TreeMap, LinkedHashMap

Map, anahtar-değer (key-value) çiftlerini tutan bir yapı. Her anahtar benzersiz (unique), her anahtar bir değere karşılık gelir. Collection interface'ini implement etmez ama Collections Framework'ün en önemli parçalarından biri.

Bunu gerçek bir sözlük gibi düşün. Kelimeyi (key) ararsın, anlamını (value) bulursun. Her kelime bir kere var ama farklı kelimeler aynı anlama sahip olabilir. Map'te de her key bir kere var ama farklı key'ler aynı value'ya sahip olabilir.

Map Temelleri

Map<String, Integer> ages = new HashMap<>();

// Ekleme
ages.put("Ali", 25);
ages.put("Veli", 30);
ages.put("Ayşe", 28);
ages.put("Ali", 26);  // Ali zaten var — value GÜNCELLENIR (25 → 26)

// Erişim
int age = ages.get("Ali");         // 26
Integer age2 = ages.get("Kemal");  // null — key yok
int age3 = ages.getOrDefault("Kemal", 0); // 0 — key yoksa default

// Sorgulama
ages.containsKey("Veli");   // true
ages.containsValue(30);      // true
ages.size();                  // 3
ages.isEmpty();               // false

// Silme
ages.remove("Veli");          // Veli silindi
ages.remove("Ali", 99);      // Silinmez! Value eşleşmeli (26 ≠ 99)
ages.remove("Ali", 26);      // Silindi — key VE value eşleşti

⚠️ null dönüşü: get() null döndüğünde iki anlam var: key yok VEYA key var ama value null. Ayrım yapmak için containsKey() kullan.

Map<String, String> map = new HashMap<>();
map.put("key1", null); // Value olarak null

map.get("key1");     // null — var ama value null
map.get("key2");     // null — key yok

map.containsKey("key1"); // true — var
map.containsKey("key2"); // false — yok

Map Üzerinde Gezinme

Map'te gezinmenin birkaç yolu var:

Map<String, Integer> scores = Map.of("Ali", 90, "Veli", 85, "Ayşe", 95);

// 1. keySet — sadece key'ler
for (String name : scores.keySet()) {
    System.out.println(name + " → " + scores.get(name));
}

// 2. values — sadece değerler
for (int score : scores.values()) {
    System.out.println(score);
}

// 3. entrySet — key-value çiftleri (EN VERİMLİ)
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.println(entry.getKey() + " → " + entry.getValue());
}

// 4. forEach (Java 8+)
scores.forEach((name, score) -> 
    System.out.println(name + " → " + score)
);

💡 İpucu: Hem key hem value lazımsa entrySet() kullan. keySet() ile dolaşıp her seferinde get(key) çağırmak ekstra lookup demek.

HashMap İç Yapısı

HashMap, hash tablosu kullanır. Key'in hashCode() değerine göre bir "bucket"a (kova) yerleştirir.

Bucket[0] → null
Bucket[1] → [Ali:25] → [Kemal:30]  (hash collision — linked list)
Bucket[2] → null
Bucket[3] → [Veli:28]
...

Java 8+: Bir bucket'taki eleman sayısı 8'i geçerse, linked list kırmızı-siyah ağaca dönüşür (O(n) → O(log n)).

Performans:

OperasyonOrtalamaEn kötü
putO(1)O(n)
getO(1)O(n)
removeO(1)O(n)
containsKeyO(1)O(n)
containsValueO(n)O(n)

"En kötü" durum tüm key'lerin aynı bucket'a düşmesi (kötü hashCode).

HashMap ve null

Map<String, Integer> map = new HashMap<>();

// null KEY — HashMap'te BİR tane null key olabilir
map.put(null, 100);
System.out.println(map.get(null)); // 100

// null VALUE — istediğin kadar
map.put("Ali", null);
map.put("Veli", null);

TreeMap: Sıralı Map

TreeMap, key'leri doğal sırada (natural ordering) veya Comparator'a göre sıralı tutar.

Map<String, Integer> sorted = new TreeMap<>();
sorted.put("Zeynep", 28);
sorted.put("Ali", 25);
sorted.put("Mehmet", 30);
System.out.println(sorted); // {Ali=25, Mehmet=30, Zeynep=28} — alfabetik

Map<Integer, String> numMap = new TreeMap<>();
numMap.put(3, "üç");
numMap.put(1, "bir");
numMap.put(2, "iki");
System.out.println(numMap); // {1=bir, 2=iki, 3=üç} — sayısal sıra

TreeMap performansı: tüm operasyonlar O(log n). HashMap'ten yavaş ama sıralama garantisi var.

// TreeMap null key KABUL ETMEZ
Map<String, Integer> tree = new TreeMap<>();
// tree.put(null, 1); // NullPointerException!

// Ters sıralama
Map<String, Integer> reversed = new TreeMap<>(Comparator.reverseOrder());
reversed.put("Ali", 25);
reversed.put("Veli", 30);
System.out.println(reversed); // {Veli=30, Ali=25}

LinkedHashMap: Ekleme Sırasını Koruyan Map

Map<String, Integer> linked = new LinkedHashMap<>();
linked.put("Üçüncü", 3);
linked.put("Birinci", 1);
linked.put("İkinci", 2);
System.out.println(linked); // {Üçüncü=3, Birinci=1, İkinci=2} — ekleme sırası

// Access-order mode (erişim sırasına göre)
Map<String, Integer> accessOrder = new LinkedHashMap<>(16, 0.75f, true);
accessOrder.put("A", 1);
accessOrder.put("B", 2);
accessOrder.put("C", 3);
accessOrder.get("A");      // A'ya erişildi → sona taşınır
System.out.println(accessOrder); // {B=2, C=3, A=1}

Access-order mode, LRU cache implementasyonu için mükemmel.

Üç Map Karşılaştırması

ÖzellikHashMapTreeMapLinkedHashMap
SıralamaYokKey'e göre sıralıEkleme sırası
put/getO(1)O(log n)O(1)
null key✅ (1 tane)✅ (1 tane)
null value
Thread-safe

Java 8+ Map Method'ları

Java 8 ile Map'e çok güçlü method'lar eklendi:

Map<String, Integer> scores = new HashMap<>();
scores.put("Ali", 90);
scores.put("Veli", 85);

// putIfAbsent — yoksa ekle, varsa dokunma
scores.putIfAbsent("Ali", 100);  // Ali var — değişmez (90 kalır)
scores.putIfAbsent("Ayşe", 88);  // Ayşe yok — eklendi

// getOrDefault — yoksa default dön
int score = scores.getOrDefault("Kemal", 0); // 0

// replace — varsa değiştir
scores.replace("Ali", 95);          // Ali varsa 95 yap
scores.replace("Ali", 95, 100);     // Ali=95 ise 100 yap (koşullu)

// compute — key'e göre hesapla
scores.compute("Ali", (key, val) -> val + 10);  // Ali: 95 → 105

// computeIfAbsent — yoksa hesapla ve ekle
scores.computeIfAbsent("Can", key -> key.length() * 10); // Can → 30

// computeIfPresent — varsa hesapla
scores.computeIfPresent("Ali", (key, val) -> val * 2); // Ali: 105 → 210

// merge — birleştir
scores.merge("Ali", 10, Integer::sum);   // Ali varsa: mevcut + 10
scores.merge("Deniz", 50, Integer::sum); // Deniz yoksa: 50

Yaygın Pattern: Sayma (Counting)

String text = "hello world hello java hello";
String[] words = text.split(" ");

// Eski yöntem
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
    if (counts.containsKey(word)) {
        counts.put(word, counts.get(word) + 1);
    } else {
        counts.put(word, 1);
    }
}

// Java 8 — merge ile
Map<String, Integer> counts2 = new HashMap<>();
for (String word : words) {
    counts2.merge(word, 1, Integer::sum);
}

// Java 8 — getOrDefault ile
Map<String, Integer> counts3 = new HashMap<>();
for (String word : words) {
    counts3.put(word, counts3.getOrDefault(word, 0) + 1);
}

System.out.println(counts2); // {java=1, world=1, hello=3}

Yaygın Pattern: Gruplama (Grouping)

List<String> names = List.of("Ali", "Ayşe", "Ahmet", "Veli", "Volkan");

// İlk harfe göre grupla
Map<Character, List<String>> grouped = new HashMap<>();
for (String name : names) {
    char firstChar = name.charAt(0);
    grouped.computeIfAbsent(firstChar, k -> new ArrayList<>()).add(name);
}
// {A=[Ali, Ayşe, Ahmet], V=[Veli, Volkan]}

// Stream ile daha temiz
Map<Character, List<String>> grouped2 = names.stream()
    .collect(Collectors.groupingBy(name -> name.charAt(0)));

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

// 10 çifte kadar
Map<String, Integer> map = Map.of(
    "Ali", 25,
    "Veli", 30,
    "Ayşe", 28
);

// 10'dan fazla entry
Map<String, Integer> bigMap = Map.ofEntries(
    Map.entry("Ali", 25),
    Map.entry("Veli", 30),
    Map.entry("Ayşe", 28),
    Map.entry("Can", 22)
);

// Değiştirilemez
// map.put("Kemal", 35); // UnsupportedOperationException!

// Map.copyOf (Java 10+)
Map<String, Integer> copy = Map.copyOf(map);

⚠️ Map.of() kuralları: Null key yasak, null value yasak, duplicate key yasak (IllegalArgumentException fırlar).

Map ve equals/hashCode

HashMap key'leri için hashCode/equals kuralları, HashSet ile aynı:

// Kendi sınıfını key olarak kullanacaksan
public class ProductId {
    private final String category;
    private final int number;
    
    // Constructor...
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProductId)) return false;
        ProductId that = (ProductId) o;
        return number == that.number && category.equals(that.category);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(category, number);
    }
}

Map<ProductId, String> products = new HashMap<>();
products.put(new ProductId("ELK", 1), "Laptop");
products.get(new ProductId("ELK", 1)); // "Laptop" — equals/hashCode doğru çalışıyor

Özet

  • Map anahtar-değer çiftleri tutar — her key unique, value tekrar edebilir

  • HashMap en hızlı (O(1)), sıra garantisi yok, 1 null key kabul eder

  • TreeMap key'lere göre sıralı (O(log n)), null key kabul etmez

  • LinkedHashMap ekleme sırasını korur, access-order mode ile LRU cache yapılabilir

  • Java 8+ method'ları (merge, compute, putIfAbsent) kodu çok sadeleştirir — sayma ve gruplama pattern'larında kullan

  • Key olarak kendi sınıfını kullanacaksan hashCode() ve equals() override etmek zorunlu