Takip et

Java’da Haritalar (Maps) Nedir ve Neden Kullanılır?

Java’da Haritalar (Maps) Nedir ve Neden Kullanılır? Java programlamada veri yapıları, bilgiyi düzenli bir şekilde depolamak ve yönetmek için

Java’da Haritalar (Maps) Nedir ve Neden Kullanılır?

Java programlamada veri yapıları, bilgiyi düzenli bir şekilde depolamak ve yönetmek için temel taşlardır. Listeler (List) ve Kümeler (Set) gibi yaygın veri yapılarının yanı sıra, anahtar-değer çiftleri halinde veri depolamanızı sağlayan özel bir yapı daha vardır: Haritalar (Map).

Bir harita, her biri benzersiz bir anahtarla ilişkilendirilmiş bir değer koleksiyonudur. Tıpkı bir sözlük gibi düşünebilirsiniz; her kelime (anahtar) kendi tanımına (değer) sahiptir. Bu yapı, belirli bir anahtara hızlı bir şekilde erişim sağlamak istediğiniz durumlarda son derece faydalıdır. Örneğin, bir kullanıcının ID’sine göre kullanıcı nesnesine, bir ülkenin koduna göre tam adına veya bir ürünün barkoduna göre fiyatına erişmek için haritalar idealdir.

Haritalar, listelerden farklı olarak öğelere indeks yerine anahtarlar aracılığıyla erişim sağlar. Bu, dinamik ve esnek veri modelleri oluşturmak için güçlü bir araçtır ve birçok gerçek dünya uygulamasının temelini oluşturur.

Java’daki Temel Map Türleri

Java’da Map bir arayüzdür ve bu arayüzü uygulayan çeşitli sınıflar bulunur. Her sınıfın kendine özgü özellikleri, performans karakteristikleri ve kullanım senaryoları vardır. En yaygın kullanılan üç türü inceleyelim:

HashMap

HashMap, Java’daki en sık kullanılan Map uygulamasıdır. Anahtarların ve değerlerin depolanması için karma (hashing) mekanizmasını kullanır. Bu sayede, eleman ekleme, silme ve erişim işlemleri ortalama olarak sabit zamanda (O(1)) gerçekleşir, bu da onu çok hızlı yapar.

  • Sıralama Garantisi Yoktur: Elemanların eklenme sırasını veya herhangi bir doğal sıralamayı korumaz. Yineleme sırasında elemanların sırası tahmin edilemez.
  • null Anahtar ve Değer: Bir tane null anahtar ve birden fazla null değer kabul edebilir.
  • Senkronize Değildir: Çoklu iş parçacıklı (multi-threaded) ortamlarda güvenli değildir. Eğer çoklu iş parçacığı erişimi gerekiyorsa ConcurrentHashMap kullanılmalıdır.

LinkedHashMap

LinkedHashMap, HashMap‘in tüm özelliklerini taşır ancak ek olarak elemanların eklenme sırasını (insertion order) korur. Yani, harita üzerinde yineleme yaptığınızda elemanlar eklendikleri sıraya göre döner. İsteğe bağlı olarak, elemanların son erişim sırasına göre de sıralanabilir (access order), bu da LFU/LRU önbellekleri gibi senaryolar için kullanışlıdır.

  • Sıralama Garantisi Var: Elemanların eklenme sırasını korur.
  • Performans: HashMap‘e göre biraz daha yavaştır, çünkü sıralamayı korumak için çift yönlü bir bağlı liste (doubly linked list) yapısı kullanır.

TreeMap

TreeMap, anahtarları doğal sıralarına göre veya bir Comparator aracılığıyla belirtilen özel bir sıralamaya göre sıralar. Bu, haritadaki elemanlara her zaman sıralı bir şekilde erişmek istediğiniz durumlarda kullanışlıdır.

  • Sıralama Garantisi Var: Anahtarları sıralı bir şekilde tutar.
  • null Anahtar Kabul Etmez: Anahtarların sıralanabilmesi gerektiği için null anahtarlara izin vermez. Ancak null değer kabul edebilir.
  • Performans: Ekleme, silme ve erişim işlemleri O(log N) zaman karmaşıklığına sahiptir, bu da HashMap‘ten daha yavaştır ancak sıralı erişim sağlar.

Map Temel İşlemleri ve Kullanımı

Java’da bir harita oluşturmak, eleman eklemek, bunlara erişmek ve üzerinde çeşitli işlemler yapmak oldukça basittir. İşte temel işlemlerin bir özeti:

Map Oluşturma

Harita oluşturmak için genellikle HashMap sınıfını kullanırız. Anahtar ve değer tiplerini belirtmeyi unutmayın.


import java.util.HashMap;
import java.util.Map;

public class MapKullanimi {
    public static void main(String[] args) {
        // String anahtar ve Integer değer tutan bir HashMap oluşturma
        Map ogrenciNotlari = new HashMap<>();
        System.out.println("Başlangıçta harita boş mu? " + ogrenciNotlari.isEmpty()); // true
    }
}

Eleman Ekleme (put)

Bir anahtar-değer çiftini haritaya eklemek için put() metodunu kullanırız. Eğer anahtar zaten varsa, eski değer yeni değerle değiştirilir ve eski değer döndürülür.


// Eleman ekleme
ogrenciNotlari.put("Ali", 85);
ogrenciNotlari.put("Ayşe", 92);
ogrenciNotlari.put("Mehmet", 78);
ogrenciNotlari.put("Zeynep", 92); // Farklı anahtar, aynı değer olabilir

System.out.println("Öğrenci Notları: " + ogrenciNotlari); // {Ali=85, Zeynep=92, Ayşe=92, Mehmet=78} - Sıra garanti değil

Elemanlara Erişim (get)

Belirli bir anahtarla ilişkili değeri almak için get() metodunu kullanırız. Eğer anahtar bulunamazsa null döner.


// Elemana erişim
int aliNotu = ogrenciNotlari.get("Ali");
System.out.println("Ali'nin Notu: " + aliNotu); // 85

// Olmayan bir anahtarı deneme
Integer canNotu = ogrenciNotlari.get("Can");
System.out.println("Can'ın Notu: " + canNotu); // null

// getOrDefault() ile varsayılan değer kullanma
int fatmaNotu = ogrenciNotlari.getOrDefault("Fatma", 0);
System.out.println("Fatma'nın Notu (varsayılan ile): " + fatmaNotu); // 0

Eleman Kontrolü (containsKey, containsValue)

Bir haritanın belirli bir anahtarı veya değeri içerip içermediğini kontrol edebilirsiniz.


// Anahtar kontrolü
boolean aliVarMi = ogrenciNotlari.containsKey("Ali");
System.out.println("Ali haritada var mı? " + aliVarMi); // true

boolean ahmetVarMi = ogrenciNotlari.containsKey("Ahmet");
System.out.println("Ahmet haritada var mı? " + ahmetVarMi); // false

// Değer kontrolü
boolean doksanIkiVarMi = ogrenciNotlari.containsValue(92);
System.out.println("92 notu haritada var mı? " + doksanIkiVarMi); // true

Eleman Silme (remove)

Belirli bir anahtar-değer çiftini haritadan silmek için remove() metodunu kullanırız. Silinen değer döndürülür. Anahtar bulunamazsa null döner.


// Eleman silme
Integer silinenNot = ogrenciNotlari.remove("Mehmet");
System.out.println("Silinen Mehmet'in Notu: " + silinenNot); // 78
System.out.println("Güncel Öğrenci Notları: " + ogrenciNotlari); // {Ali=85, Zeynep=92, Ayşe=92}

// Olmayan bir anahtarı silmeye çalışmak
Integer olmayanSilinen = ogrenciNotlari.remove("Can");
System.out.println("Olmayan Can'ın Notu: " + olmayanSilinen); // null

Boyut ve Temizleme (size, isEmpty, clear)

Haritanın kaç eleman içerdiğini, boş olup olmadığını ve tüm elemanlarını temizlemeyi sağlayan metodlar.


System.out.println("Haritanın boyutu: " + ogrenciNotlari.size()); // 3
System.out.println("Harita boş mu? " + ogrenciNotlari.isEmpty()); // false

ogrenciNotlari.clear(); // Tüm elemanları siler
System.out.println("Harita temizlendikten sonra: " + ogrenciNotlari); // {}
System.out.println("Harita boş mu? " + ogrenciNotlari.isEmpty()); // true

Map Üzerinde Yineleme (Iterating)

Haritadaki elemanlar üzerinde dolaşmak için birkaç farklı yöntem bulunur. İhtiyacınıza göre en uygun olanı seçebilirsiniz.

Anahtarlar Üzerinden Yineleme (keySet())

Sadece anahtarlar üzerinde yineleme yapmak istiyorsanız keySet() metodunu kullanabilirsiniz. Bu metod, haritadaki tüm anahtarları içeren bir Set döndürür.


Map ulkeBaskentleri = new HashMap<>();
ulkeBaskentleri.put("Türkiye", "Ankara");
ulkeBaskentleri.put("Fransa", "Paris");
ulkeBaskentleri.put("Almanya", "Berlin");

System.out.println("--- Anahtarlar Üzerinden Yineleme ---");
for (String ulke : ulkeBaskentleri.keySet()) {
    System.out.println("Ülke: " + ulke);
}

Değerler Üzerinden Yineleme (values())

Sadece değerler üzerinde yineleme yapmak istiyorsanız values() metodunu kullanabilirsiniz. Bu metod, haritadaki tüm değerleri içeren bir Collection döndürür.


System.out.println("--- Değerler Üzerinden Yineleme ---");
for (String baskent : ulkeBaskentleri.values()) {
    System.out.println("Başkent: " + baskent);
}

Girişler (Entry) Üzerinden Yineleme (entrySet())

Hem anahtarlara hem de değerlere aynı anda erişmek istiyorsanız en verimli yol entrySet() metodunu kullanmaktır. Bu metod, haritadaki her bir anahtar-değer çiftini temsil eden Map.Entry nesnelerinin bir Set‘ini döndürür.


System.out.println("--- Girişler Üzerinden Yineleme ---");
for (Map.Entry entry : ulkeBaskentleri.entrySet()) {
    System.out.println("Ülke: " + entry.getKey() + ", Başkent: " + entry.getValue());
}

Java 8 forEach() Metodu ile Yineleme

Java 8 ile birlikte gelen forEach() metodu, haritalar üzerinde yineleme yapmak için daha modern ve okunabilir bir yol sunar. Lambda ifadeleri ile birlikte kullanılır.


System.out.println("--- Java 8 forEach() ile Yineleme ---");
ulkeBaskentleri.forEach((ulke, baskent) -> 
    System.out.println("Ülke: " + ulke + ", Başkent: " + baskent)
);

Pratik İpuçları ve Gelişmiş Kullanım

Doğru Map Türünü Seçmek

  • HashMap: Çoğu genel amaçlı kullanım için varsayılan seçimdir. Yüksek performans gerektiren ve sıralamanın önemli olmadığı durumlarda tercih edilir.
  • LinkedHashMap: Elemanların eklenme sırasının korunması gerektiğinde veya LRU/LFU önbellekleri gibi erişim sırasına göre sıralamanın önemli olduğu durumlarda kullanılır.
  • TreeMap: Anahtarların doğal sıralarına göre veya özel bir Comparator ile sıralanması gerektiğinde kullanılır. Örneğin, bir anahtar aralığındaki elemanlara erişmek (subMap) istediğinizde.
  • ConcurrentHashMap: Çoklu iş parçacıklı (multi-threaded) ortamlarda güvenli harita erişimi sağlamak için kullanılır. Performans açısından Hashtable‘dan daha iyidir.

Değişmez (Immutable) Map’ler Oluşturma

Java 9 ve sonraki versiyonlarda, küçük, değişmez haritalar oluşturmak için Map.of() ve Map.ofEntries() gibi kolaylaştırıcı metodlar bulunmaktadır. Bu haritalar oluşturulduktan sonra değiştirilemezler (ekleme, silme yapılamaz). Güvenlik ve performans avantajları sunarlar.


// Değişmez Map oluşturma (Java 9+)
Map immutableMap = Map.of("Bir", 1, "İki", 2, "Üç", 3);
// immutableMap.put("Dört", 4); // UnsupportedOperationException hatası verir
System.out.println("Değişmez Map: " + immutableMap);

// Varolan bir Map'ten değişmez kopya oluşturma (Java 10+)
Map mutableMap = new HashMap<>();
mutableMap.put("A", "Apple");
Map immutableCopy = Map.copyOf(mutableMap);
// immutableCopy.put("B", "Banana"); // UnsupportedOperationException
System.out.println("Değişmez Kopya Map: " + immutableCopy);

computeIfAbsent() ve merge() gibi Modern Metodlar

Java 8 ile birlikte Map arayüzüne eklenen yeni metodlar, haritalarla çalışmayı daha kolay ve verimli hale getirir.

  • computeIfAbsent(K key, Function super K, ? extends V> mappingFunction): Eğer belirtilen anahtar haritada yoksa, fonksiyonu çalıştırarak bir değer hesaplar ve bu değeri anahtarla ilişkilendirir.
  • merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction): Eğer belirtilen anahtar haritada yoksa, değeri ekler. Eğer anahtar varsa, mevcut değer ile yeni değeri birleştirmek için belirtilen fonksiyonu kullanır.

Map kelimeSayilari = new HashMap<>();
kelimeSayilari.put("elma", 2);

// "armut" yoksa, 1 olarak ekle
kelimeSayilari.computeIfAbsent("armut", k -> 1);
System.out.println("ComputeIfAbsent sonrası: " + kelimeSayilari); // {elma=2, armut=1}

// "elma" zaten var, mevcut değer ile yeni değeri topla
kelimeSayilari.merge("elma", 1, (mevcutDeger, yeniDeger) -> mevcutDeger + yeniDeger);
System.out.println("Merge sonrası (elma): " + kelimeSayilari); // {elma=3, armut=1}

// "kiraz" yok, 1 olarak ekle
kelimeSayilari.merge("kiraz", 1, (mevcutDeger, yeniDeger) -> mevcutDeger + yeniDeger);
System.out.println("Merge sonrası (kiraz): " + kelimeSayilari); // {elma=3, armut=1, kiraz=1}

Sonuç

Java’daki haritalar, anahtar-değer çiftleri halinde veri depolamak için vazgeçilmez bir veri yapısıdır. HashMap, LinkedHashMap ve TreeMap gibi farklı türleri, çeşitli performans ve sıralama gereksinimlerine cevap verir. Temel ekleme, erişim, silme ve yineleme işlemleriyle birlikte, Java 8 ve sonrası gelen modern metodlar, haritalarla çalışmayı daha esnek ve verimli hale getirmiştir. Doğru Map türünü seçmek ve gelişmiş özelliklerini kullanmak, uygulamanızın performansını ve okunabilirliğini önemli ölçüde artıracaktır.

Sıkça Sorulan Sorular (SSS)

S: Bir Map’e null anahtar veya değer ekleyebilir miyim?

C: HashMap ve LinkedHashMap bir tane null anahtar ve birden fazla null değer kabul eder. Ancak TreeMap, anahtarların sıralanması gerektiği için null anahtarlara izin vermez.

S: Java Map’leri thread-safe (iş parçacığı güvenli) midir?

C: Hayır, HashMap, LinkedHashMap ve TreeMap varsayılan olarak thread-safe değildir. Çoklu iş parçacıklı ortamlarda kullanıldığında senkronizasyon sorunları yaşanabilir. Bu tür durumlarda java.util.concurrent paketindeki ConcurrentHashMap sınıfı kullanılmalıdır.

S: Bir Map’i nasıl sıralayabilirim?

C: Eğer anahtarlara göre sıralı erişim istiyorsanız TreeMap kullanabilirsiniz. Eğer HashMap veya LinkedHashMap‘i sıralamak istiyorsanız, entrySet()‘i bir List‘e dönüştürüp Collections.sort() metodu ile veya Java 8 Stream API’yi kullanarak anahtarlara ya da değerlere göre sıralayabilirsiniz.


// HashMap'i değerlere göre sıralama örneği
Map notlar = new HashMap<>();
notlar.put("Ali", 85);
notlar.put("Ayşe", 92);
notlar.put("Mehmet", 78);

notlar.entrySet().stream()
    .sorted(Map.Entry.comparingByValue())
    .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
// Çıktı: Mehmet: 78, Ali: 85, Ayşe: 92

S: Map.Entry nedir?

C: Map.Entry, Map arayüzünün içinde tanımlanmış bir iç arayüzdür. Bir haritadaki tek bir anahtar-değer çiftini temsil eder. entrySet() metodu, Map.Entry nesnelerinin bir kümesini döndürür ve bu sayede hem anahtara (getKey()) hem de değere (getValue()) aynı anda erişim sağlanır.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Mobil sürümden çık