HashMap'te temel varlık kontrolü


309

HashMap'te anahtar varlığını kontrol etmek her zaman gerekli mi?

1000 girişleri ile bir HashMap var ve verimliliği artırmak için arıyorum. HashMap'e çok sık erişiliyorsa, her erişimde anahtar varlığını kontrol etmek büyük bir ek yüke yol açacaktır. Bunun yerine anahtar yoksa ve bu nedenle bir istisna oluşursa, istisnayı yakalayabilirim. (bunun nadiren olacağını bildiğimde). Bu, HashMap'e erişimi yarıya indirecektir.

Bu iyi bir programlama uygulaması olmayabilir, ancak erişim sayısını azaltmama yardımcı olacaktır. Yoksa burada bir şey mi kaçırıyorum?

[ Güncelleme ] HashMap içinde null değer yok.


8
"dolayısıyla ve istisna oluşur" - ne istisnası? Bu java.util.HashMap olmayacak ...
serg10

Yanıtlar:


513

Hiç boş değer depoladınız mı? Değilse, şunları yapabilirsiniz:

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // No such key
}

Aksi takdirde, bir boş değer döndürürseniz varlığını kontrol edebilirsiniz :

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // Key might be present...
    if (map.containsKey(key)) {
       // Okay, there's a key but the value is null
    } else {
       // Definitely no such key
    }
}

1
@Samuel: Yalnızca null olası bir değer olduğunda. Haritada kesinlikle null değerleriniz yoksa, sadece getiyidir ve değere ihtiyacınız olduğunda iki arama yapmaktan kaçınır.
Jon Skeet

Her ne kadar bu örnek olarak daha açık olsa da if(value!=null || map.containsKey(key)), ikinci bölüm için de yazabilirsiniz . En azından aynı şeyi her iki şekilde yapmak istiyorsanız - tekrarlanan kod yok. Kısa devre nedeniyle çalışacaktır .
Cullub

66

Anahtarın var olup olmadığını kontrol ederek hiçbir şey kazanamazsınız. Bu kod HashMap:

@Override
public boolean containsKey(Object key) {
    Entry<K, V> m = getEntry(key);
    return m != null;
}

@Override
public V get(Object key) {
    Entry<K, V> m = getEntry(key);
    if (m != null) {
        return m.value;
    }
    return null;
}

Bunun için dönüş değerinin get()farklı olup olmadığını kontrol etmeniz yeterlidir null.

Bu HashMap kaynak kodudur.


Kaynaklar:


2
Bu yöntemlerin belirli bir uygulamasını göstermenin anlamı nedir?
jarnbjo

2
Çoğu durumda, anahtarın var olup olmadığını kontrol etmek, değeri elde etmekle aynı zaman alacaktır. Bu nedenle, değeri almadan önce anahtarın gerçekten var olduğunu kontrol etmek için hiçbir şey optimize edilmez. Bunun bir genelleme olduğunu biliyorum ama anlamaya yardımcı olabilir.
Colin Hebert

İyi bir bağlantı grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… (OpenJDK, Sun kodundan çok güçlü bir şekilde türetilmiştir) ve yanıldığım anlaşılıyor. Java5 ile Java6 sürümünü karşılaştırıyordum; bu alanda farklı çalışırlar (ancak gönderdiğiniz parçacıklar gibi ikisi de doğrudur).
Donal Fellows

2
Kabul edilen cevapta belirtildiği gibi, bu salyangoz açıkça yanlıştır. Tabii ki, değerleri karşılaştıran anahtar varlığını kontrol ederek bir şeyler kazanırsınız - var olmayan anahtarları mevcut anahtarlardan ayırt edebilir, ancak bir değer olarak null ile eşleştirebilirsiniz.
Johannes H.

43

Daha iyi bir yol containsKeyyöntemi kullanmaktır HashMap. Yarın birisi Haritaya null ekleyecek. Anahtar varlığı ve anahtar null değerine sahip olmalıdır.


Evet. Veya nullbirlikte depolamayı önlemek için HashMap'in alt sınıfını yapın .
RobAu

1
Bu cevabı kullanarak gereksiz gereksiz değer olarak ilkel tipler için 1+ gerekli değildir
Prashant Bhanarkar 23:16

.ContainsKey () yazmak null olup olmadığını kontrol etmekten daha akıcıdır. Okunması kolay olma konusunda endişelenmeliyiz, bu da çoğu zaman küçük bir optimizasyondan çok geliştiricilere zaman kazandırır. En azından gerekli hale gelmeden önce optimizasyon yapmayın.
Maksimum

23

Yani kodun olduğu anlamına mı geliyor?

if(map.containsKey(key)) doSomethingWith(map.get(key))

her yerde ? Sonra map.get(key)null döndürülüp döndürülmediğini kontrol etmelisiniz ve hepsi bu. Bu arada, HashMap eksik anahtarlar için istisnalar atmaz, bunun yerine null değerini döndürür. Gerekli olan tek durum containsKey, null değerleri saklarken, null değeri ve eksik değeri birbirinden ayırmaktır, ancak bu genellikle kötü uygulama olarak kabul edilir.


8

Sadece containsKey()netlik için kullanın . Hızlıdır ve kodu temiz ve okunabilir tutar. Bütün mesele HashMaps anahtar arama hızlı olduğundan, sadece emin olmak olduğunu hashCode()ve equals()düzgün uygulanmaktadır.


4
if(map.get(key) != null || (map.get(key) == null && map.containsKey(key)))

3

computeIfAbsent()Yöntemi HashMapsınıfta da kullanabilirsiniz .

Aşağıdaki örnekte, mapanahtara (banka hesabının adı) uygulanan işlemlerin (tamsayılar) bir listesini saklar. İçin 100ve 2 işlem eklemek 200için checking_accountşunları yazabilirsiniz:

HashMap<String, ArrayList<Integer>> map = new HashMap<>();
map.computeIfAbsent("checking_account", key -> new ArrayList<>())
   .add(100)
   .add(200);

Bu şekilde, anahtarın checking_accountvar olup olmadığını kontrol etmek zorunda kalmazsınız .

  • Eğer mevcut değilse, biri lambda ifadesi tarafından yaratılacak ve geri döndürülecektir.
  • Varsa, anahtarın değeri tarafından döndürülür computeIfAbsent().

Gerçekten zarif! 👍


0

Genellikle deyimi kullanırım

Object value = map.get(key);
if (value == null) {
    value = createValue(key);
    map.put(key, value);
}

Bu, anahtar eksikse haritaya yalnızca iki kez vurduğunuz anlamına gelir


0
  1. Anahtar sınıf sizin ise hashCode () ve equals () yöntemlerinin uygulandığından emin olun.
  2. Temelde HashMap'e erişim O (1) olmalıdır, ancak yanlış hashCode yöntemi uygulaması ile O (n) olur, çünkü aynı karma anahtarına sahip değer Bağlantılı liste olarak saklanır.

0

Jon Skeet yanıtı iki senaryoyu ( nulldeğerle değil nulldeğerle harita ) verimli bir şekilde ele alıyor.

Sayı girişleri ve verimlilik endişesi hakkında, bir şeyler eklemek istiyorum.

1.000 giriş ile bir HashMap var ve verimliliği artırmak için arıyorum. HashMap'e çok sık erişiliyorsa, her erişimde anahtar varlığını kontrol etmek büyük bir ek yüke yol açacaktır.

1.000 girişli bir harita çok büyük bir harita değildir.
Ayrıca 5.000 veya 10.000 girişe sahip bir harita.
Mapbu boyutlarla hızlı erişim sağlamak için tasarlanmıştır.

Şimdi, hashCode()harita tuşlarının iyi bir dağılım sağladığını varsayar .

IntegerAnahtar türü olarak bir anahtar kullanabilirsiniz .
Onun hashCode()çarpışmalar benzersiz mümkün olmadığından yöntem çok etkilidir intdeğerler:

public final class Integer extends Number implements Comparable<Integer> {
    ...
    @Override
    public int hashCode() {
        return Integer.hashCode(value);
    }

    public static int hashCode(int value) {
        return value;
    }
    ...
}

Anahtar için, Stringörneğin sıklıkla kullanılan başka bir yerleşik tür kullanmanız gerekiyorsa Map, bazı çarpışmalar olabilir, ancak 1 binden binlerce nesneye Mapkadar, String.hashCode()yöntem olarak çok azına sahip olmalısınız. iyi bir dağıtım sağlar.

Özel bir tür kullanırsanız, geçersiz kılın hashCode()ve equals()doğru şekilde ayarlayın ve genel olarak hashCode()adil bir dağıtım sağlandığından emin olun .
Atıfta bulunan 9 Java Effectiveuncu maddeye başvurabilirsiniz .
İşte bu mesaj yolu ayrıntıları.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.