Map.get () kullanılırken java Map.containsKey () gereksiz kullanıyor


93

Bir süredir, en iyi uygulama içinde containsKey()yöntemi kullanmaktan kaçınmanın java.util.Mapve bunun yerine sonuç üzerinde boş bir kontrol yapmanın izin verilip verilmediğini merak ediyordum get().

Benim mantığım, değerin iki kez aranmasının gereksiz görünmesidir - önce için containsKey()ve sonra tekrar get().

Diğer yandan, çoğu standart Mapönbellek uygulamasının son aramayı yapması veya derleyicinin fazlalıktan başka türlü kurtulabilmesi ve kodun okunabilirliği için containsKey()parçanın korunmasının tercih edilmesi olabilir .

Yorumlarınızı çok takdir ediyorum.

Yanıtlar:


112

Bazı Harita uygulamalarının null değerlere sahip olmasına izin verilir, örneğin HashMap, bu durumda get(key)geri dönerse nullbu anahtarla ilişkili haritada herhangi bir giriş olmadığını garanti etmez.

Bir haritanın anahtar kullanımı içerip içermediğini bilmek istiyorsanız Map.containsKey. Anahtar kullanımıyla eşlenmiş bir değere ihtiyacınız varsa Map.get(key). Bu eşleme boş değerlere izin veriyorsa, boş değerin bir dönüş değeri eşlemenin anahtar için eşleşme içermediğini göstermez; Böyle bir durumda Map.containsKeyişe yaramaz ve performansı etkileyecektir. Ayrıca, bir haritaya eşzamanlı erişim durumunda (örn. ConcurrentHashMap), Test ettikten sonra Map.containsKey(key), girişin siz aramadan önce başka bir iş parçacığı tarafından kaldırılma şansı vardır Map.get(key).


8
Değer olarak ayarlanmış olsa bile null, bunu ayarlanmamış bir anahtar / değer için farklı şekilde ele almak ister misiniz? Özellikle farklı bir şekilde davranmanız gerekmiyorsa, kullanabilirsinizget()
Peter Lawrey

1
Eğer öyleyse Map, privatesınıfınız a'nın nullharitaya asla eklenmemesini garanti edebilir . Bu durumda, get()yerine null için bir kontrol kullanabilirsiniz containsKey(). Bunu yapmak, bazı durumlarda daha net ve belki biraz daha verimli olabilir.
Raedwald 13

44

Yazmanın oldukça standart olduğunu düşünüyorum:

Object value = map.get(key);
if (value != null) {
    //do something with value
}

onun yerine

if (map.containsKey(key)) {
    Object value = map.get(key);
    //do something with value
}

Daha az okunaklı ve biraz daha verimli değil, bu yüzden yapmamak için herhangi bir neden görmüyorum. Açıktır ki , haritanız boş değer içeriyorsa, iki seçenek aynı anlamsallığa sahip değildir .


8

Asililerin de belirttiği gibi, bu anlamsal bir sorudur. Genel olarak, Map.get (x) == null istediğiniz şeydir, ancak containsKey'i kullanmanın önemli olduğu durumlar da vardır.

Böyle bir durum önbellektir. Bir keresinde, veritabanını sorgulayan ve var olmayan varlıkları arayan bir web uygulamasında bir performans sorunu üzerinde çalıştım. Bu bileşenin önbelleğe alma kodunu incelediğimde, cache.get (key) == null ise veritabanını sorguladığını fark ettim. Veritabanı null döndürdüyse (varlık bulunamadı), bu anahtarı -> boş eşlemeyi önbelleğe alacağız.

ContainsKey'e geçmek sorunu çözdü çünkü boş bir değere eşleme aslında bir şey ifade ediyordu. Null'a anahtar eşleme, mevcut olmayan anahtardan farklı bir anlamsal anlama sahipti.


İlginç. Değerleri önbelleğe almadan önce neden boş bir denetim eklemediniz?
Saket

Bu hiçbir şeyi değiştirmez. Nokta sıfır araçlarına anahtar haritalama "biz zaten bunu yapmış. Önbelleğe oluyor. Değeri olmasıdır olan boş". Verilen bir anahtarı hiç içermemesine karşılık, "Bilmiyorum, önbellekte değil, DB'yi kontrol etmemiz gerekebilir."
Brandon

5
  • containsKeyve ardından a get, yalnızca boş değerlere asla izin verilmeyeceğini önceden biliyorsak gereksizdir. Boş değerler geçerli değilse, çağrısının containsKeyönemsiz olmayan bir performans cezası vardır ve aşağıdaki karşılaştırmada gösterildiği gibi sadece ek yüktür.

  • Java 8 Optionaldeyimleri - Optional.ofNullable(map.get(key)).ifPresentveya Optional.ofNullable(map.get(key)).ifPresent- sadece vanilya boş kontrollerine kıyasla önemsiz olmayan bir ek yüke maruz kalır.

  • Bir HashMapbir kullanan O(1)bir ise sabit bir tablo arama TreeMapkullanımlar bir O(log(n))arama. containsKeyBir izledi getbir üzerinde çağrıldığında deyim çok yavaştır TreeMap.

Kıyaslamalar

Bkz. Https://github.com/vkarun/enum-reverse-lookup-table-jmh

// t1
static Type lookupTreeMapNotContainsKeyThrowGet(int t) {
  if (!lookupT.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupT.get(t);
}
// t2
static Type lookupTreeMapGetThrowIfNull(int t) {
  Type type = lookupT.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// t3
static Type lookupTreeMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupT.get(t)).orElseThrow(() -> new 
      IllegalStateException("Unknown Multihash type: " + t));
}
// h1
static Type lookupHashMapNotContainsKeyThrowGet(int t) {
  if (!lookupH.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupH.get(t);
}
// h2
static Type lookupHashMapGetThrowIfNull(int t) {
  Type type = lookupH.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// h3
static Type lookupHashMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupH.get(t)).orElseThrow(() -> new 
    IllegalStateException("Unknown Multihash type: " + t));
}
Kıyaslama (yinelemeler) (arama yaklaşımı) Modu Cnt Puanı Hata Birimleri

MultihashTypeLookupBenchmark.testLookup 1000 t1 ortalama 9 33.438 ± 4.514 us / op
MultihashTypeLookupBenchmark.testLookup 1000 t2 ortalama 9 26.986 ± 0.405 us / op
MultihashTypeLookupBenchmark.testLookup 1000 t3 ortalama 9 39.259 ± 1.306 us / op
MultihashTypeLookupBenchmark.testLookup 1000 sa1 ortalama 9 18.954 ± 0.414 us / op
MultihashTypeLookupBenchmark.testLookup 1000 h2 ortalama 9 15.486 ± 0.395 us / op
MultihashTypeLookupBenchmark.testLookup 1000 h3 ortalama 9 16.780 ± 0.719 us / op

TreeMap kaynak referansı

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/TreeMap.java

HashMap kaynak referansı

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/HashMap.java


3

@Assylias yanıtını Java8 İsteğe Bağlı ile daha okunabilir hale getirebiliriz,

Optional.ofNullable(map.get(key)).ifPresent(value -> {
     //do something with value
};)

2

Java'da uygulamayı kontrol ederseniz

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

her ikisi de ana işin yapıldığı eşleştirmeyi almak için getNode kullanır.

Fazlalık bağlamsaldır, örneğin karma haritada depolanan bir sözlüğünüz varsa. Bir kelimenin anlamını öğrenmek istediğinizde

yapıyor ...

if(dictionary.containsKey(word)) {
   return dictionary.get(word);
}

gereksizdir.

ancak bir kelimenin geçerli olup olmadığını kontrol etmek istiyorsanız sözlüğe göre. yapıyor ...

 return dictionary.get(word) != null;

bitmiş...

 return dictionary.containsKey(word);

gereksizdir.

HashMap'i dahili olarak kullanan HashSet uygulamasını kontrol ederseniz , 'içerir' yönteminde 'containsKey' kullanın.

    public boolean contains(Object o) {
        return map.containsKey(o);
    }
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.