HashMapでのキーの存在チェック


309

HashMapでキーの存在を確認することは常に必要ですか?

私は、たとえば1000エントリのHashMapを持っていて、効率を改善しようとしています。HashMapが非常に頻繁にアクセスされる場合、すべてのアクセスでキーの存在を確認すると、大きなオーバーヘッドが発生します。キーが存在しないために例外が発生した場合は、代わりに例外をキャッチできます。(これがめったに起こらないことを知っているとき)。これにより、HashMapへのアクセスが半分になります。

これは良いプログラミング方法ではないかもしれませんが、アクセス回数を減らすのに役立ちます。または、ここで何か不足していますか?

[ 更新 ] HashMapにnull値がありません。


8
「したがって、例外が発生します」-どのような例外ですか?これはjava.util.HashMapからのものではありません...
serg10

回答:


513

null値を保存したことはありますか?そうでない場合は、単に行うことができます:

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

それ以外の場合は、null値が返された場合に存在を確認するだけです。

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:nullが可能な値の場合のみ。マップに確実にnull値がない場合は問題ありませんget。値が必要なときに2つのルックアップを行わないようにします。
Jon Skeet

これは例としておそらくより明確ですがif(value!=null || map.containsKey(key))、第2部のために書くこともできます。少なくとも、同じことをどちらかの方法で行いたい場合は、コードを繰り返す必要はありません。それは短絡のために機能します。
Cullub

66

キーが存在することを確認しても何も得られません。これはのコードです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;
}

の戻り値get()が異なるかどうかを確認するだけですnull

これはHashMapのソースコードです。


リソース:


2
これらのメソッドの1つの特定の実装を示すポイントは何ですか?
jarnbjo

2
これを説明するために、ほとんどの場合、キーが存在することを確認することは、値を取得することとほぼ同じ時間がかかります。そのため、値を取得する前にキーが実際に存在するかどうかを確認するための最適化は行われません。それが一般化されていることは知っていますが、理解するのに役立ちます。
Colin Hebert

良いリンクはgrepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…(OpenJDKはSunのコードから非常に強く派生しています)であり、私は間違っているようです。Java5とJava6のバージョンを比較していました。この領域での動作は異なります(ただし、投稿したスニペットと同様、どちらも正しいです)。
ドナルフェロー

2
受け入れられた回答で指摘されているように、この刺し傷は明らかに間違っています。もちろん、キーの存在をチェックして値を比較することによって何かを得ます-存在しないキーを、存在するが値としてnullにマップされているキーと区別できます。
ヨハネスH.

43

より良い方法は、のcontainsKeyメソッドを使用することですHashMap。明日誰かがマップにnullを追加します。キーの存在とキーがnull値であることを区別する必要があります。


うん。または、HashMapをサブクラス化して、すべてが格納されないようにしnullます。
RobAu 2015

1
この回答では不要なキャストは不要なので、プリミティブ型の1+
Prashant Bhanarkar

また、nullをチェックするよりも.containsKey()を書く方が流暢です。ほとんどの場合、マイナーな最適化よりも、読みやすく、開発者の時間を節約できることを心配する必要があります。少なくとも、必要になる前に最適化しないでください。
最大

23

次のようなコードがあるということですか

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

あらゆる所に ?次に、単にmap.get(key)nullが返されたかどうかを確認するだけです。ちなみに、HashMapは欠落したキーの例外をスローせず、代わりにnullを返します。containsKey必要な唯一のケースは、null値と欠損値を区別するためにnull値を格納している場合ですが、これは通常、悪い習慣と見なされます。


8

containsKey()わかりやすくするために使用します。高速であり、コードをクリーンで読みやすい状態に保ちます。HashMapsの重要な点は、キーの検索が高速であることです。hashCode()equals()が適切に実装されていることを確認してください。



3

クラスでcomputeIfAbsent()メソッドを使用することもできHashMapます。

次の例でmapは、キー(銀行口座の名前)に適用されるトランザクション(整数)のリストを格納します。の2つのトランザクションを追加するには100、次のように記述200checking_accountます。

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

これにより、キーchecking_accountが存在するかどうかを確認する必要がなくなります。

  • 存在しない場合は、ラムダ式によって作成されて返されます。
  • 存在する場合、キーの値はによって返されcomputeIfAbsent()ます。

本当にエレガント!👍


0

私は通常イディオムを使います

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

つまり、キーが見つからない場合、マップを2回だけヒットします。


0
  1. キークラスが自分のものである場合は、hashCode()およびequals()メソッドが実装されていることを確認してください。
  2. 基本的に、HashMapへのアクセスはO(1)ですが、hashCodeメソッドの実装が間違っていると、同じハッシュキーの値がリンクリストとして格納されるため、O(n)になります。

0

Jon Skeetの回答は、2つのシナリオを適切に処理します(null値を持つマップとないマップnullは、効率的な方法でます。

エントリー数と効率性について、何か付け加えたいと思います。

私は、例えば1.000エントリのHashMapを持っていて、効率を改善しようとしています。HashMapが非常に頻繁にアクセスされる場合、すべてのアクセスでキーの存在を確認すると、大きなオーバーヘッドが発生します。

1.000エントリのマップは、巨大なマップではありません。
同様に5.000または10.000エントリのマップ。
Mapこのような次元で高速に検索できるように設計されています。

今、それは hashCode()は、マップキーが適切に分散されます。

Integerキータイプとして使用できる場合は、それを実行してください。一意の値では衝突が発生しないため、
そのhashCode()方法は非常に効率的intです。

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;
    }
    ...
}

キーについてはString、たとえばでよく使用される別の組み込み型を使用するMap必要がある場合、いくつかの衝突が発生する可能性がありますが、のオブジェクトの数が1,000から数千Mapになると、String.hashCode()メソッド良い分布を提供します。

カスタムタイプを使用する場合は、オーバーライドhashCode()してequals()正しく、全体がhashCode()公平な分布を提供するようにしてください。
参考資料9の項目を参考にしてくださいJava Effective
これがその方法の詳細な投稿です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.