HashMapは見つからないキーのデフォルト値を返しますか?


151

HashMapセットにないすべてのキーに対してデフォルト値を返すことは可能ですか?


キーの存在を確認し、デフォルトに戻すことができます。または、クラスを拡張して動作を変更します。または、nullを使用することもできます。使用する場所にチェックを入れます。
SudhirJ 2011

2
これは関連しています/ stackoverflow.com/questions/4833336/の複製です他のいくつかのオプションがそこで議論されています。
マークバトラー

3
Map API getOrDefault() リンク
Trey Jonn

回答:


136

[更新]

他の回答やコメントで述べたように、Java 8以降では、を呼び出すだけMap#getOrDefault(...)です。

[元の]

これを正確に行うMap実装はありませんが、HashMapを拡張して独自に実装するのは簡単です。

public class DefaultHashMap<K,V> extends HashMap<K,V> {
  protected V defaultValue;
  public DefaultHashMap(V defaultValue) {
    this.defaultValue = defaultValue;
  }
  @Override
  public V get(Object k) {
    return containsKey(k) ? super.get(k) : defaultValue;
  }
}

20
正確に言うと、意図的に値を追加した場合に備えて、条件をから(v == null)に調整することができます。私は知っています、これは単なるコーナーケースですが、作者はそれに遭遇するかもしれません。(v == null && !this.containsKey(k))null
Adam Paynter、2011

@maerics:を使用していることに気付きました!this.containsValue(null)。これは微妙に異なり!this.containsKey(k)ます。containsValueいくつかの場合解決策は失敗します、他のキーが明示的に値が割り当てられていますnull。例:map = new HashMap(); map.put(k1, null); V v = map.get(k2);この場合、vまだnull正しいですか?
Adam Paynter、2009

21
一般的に、これは悪い考えだと思います。デフォルトの動作をクライアント、またはマップであると主張しないデリゲートにプッシュします。特に、有効なkeySet()またはentrySet()の欠如は、Mapコントラクトが尊重されることを期待するすべての問題を引き起こします。また、containsKey()を含む有効なキーの無限のセットは、診断が難しいパフォーマンスの低下を引き起こす可能性があります。ただし、特定の目的を果たさない可能性があることは言うまでもありません。
Ed Staub、2011

このアプローチの1つの問題は、値が複雑なオブジェクトである場合です。Map <String、List> #putは期待どおりに機能しません。
Eyal 2014年

ConcurrentHashMapでは機能しません。そこで、getの結果がnullかどうかを確認する必要があります。
dveim、2015年

162

Java 8では、Map.getOrDefaultを使用します。キーと、一致するキーが見つからない場合に返す値を受け取ります。


14
getOrDefault非常に優れていますが、マップにアクセスするたびにデフォルトの定義が必要です。デフォルト値を一度定義すると、値の静的マップを作成するときに読みやすさが向上します。
2014

3
これは自分で実装するのは簡単です。private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Jack Satriano、2015年

@JackSatrianoそうですが、デフォルト値をハードコードするか、静的変数を用意する必要があります。
Blrp 2016年

1
以下のcomputeIfAbsentを使用した回答を参照してください。デフォルト値が高価であるか、毎回異なる必要がある場合に適しています。
hectorpal 2017年

それはメモリにとってはより悪いですが、デフォルト値が構築/計算するのに費用がかかる場合にのみ計算時間を節約します。安い場合は、デフォルト値を返すだけでなく、マップに挿入する必要があるため、パフォーマンスが低下する可能性があります。確かに別のオプションです。
Spycho 2017年

73

ホイールを再発明したくない場合は、CommonsのDefaultedMapを使用してください。たとえば、

Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname"); 
// surname == "[NO ENTRY FOUND]"

そもそもマップの作成を担当していない場合は、既存のマップを渡すこともできます。


26
+1ですが、単純な機能の小さなスライスに大きな依存関係を導入するよりも、ホイールを再発明する方が簡単な場合があります。
maerics

3
面白いことに、私が取り組んでいる多くのプロジェクトには、クラスパス(Apache CommonsまたはGoogle Guavaのいずれか)にすでにこのようなものが含まれています
bartosz.r

@ bartosz.r、間違いなくモバイルではありません
Pacerier

44

Java 8は、レイジー計算された値を保存するため、インターフェイスに素敵なcomputeIfAbsentデフォルトメソッドを導入したMapため、マップコントラクトを壊しません。

Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));

起源:http : //blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/

Disclamer: この回答は、OPが尋ねたものと正確には一致しませんが、キーの数が制限されていて、異なる値のキャッシュが有益である場合に、質問のタイトルと一致する場合に便利です。大量のキーと同じデフォルト値を使用して反対のケースで使用しないでください。これにより、メモリが無駄に無駄になります。


OPが尋ねたことではありません:彼はマップに副作用を望んでいません。また、存在しないキーごとにデフォルト値を保存すると、メモリ空間が無駄に失われます。
numéro6

@numéro6、はい、これはOPが尋ねたものと正確には一致しませんが、一部のグーグルの人々はまだこの副次的な答えが役立つと感じています。他の回答が述べたように、マップコントラクトを壊さずにOPが要求したものを正確に達成することは不可能です。ここで言及されていない別の回避策は、Mapの代わりに別の抽象化使用することです。
Vadzim 2017

マップコントラクトを壊すことなく、OPが要求したものを正確に達成することが可能です。回避策は必要ありません。getOrDefaultを使用するのが正しい(最も更新された)方法であり、computeIfAbsentは間違った方法です。結果を(欠落しているキーごとに)格納することで、mappingFunctionとメモリの呼び出しに時間がかかります。getOrDefaultの代わりに、これを行う正当な理由がわかりません。私が説明しているのは、Mapコントラクトに2つの異なるメソッドがある理由です:混乱してはならない2つの異なるユースケースがあります(作業中にいくつか修正する必要がありました)。この答えは混乱を広げました。
numéro6

14

まさにこれを行う静的メソッドを作成することはできませんか?

private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
    return map.containsKey(key) ? map.get(key) : defaultValue;
}

静的をどこに保存しますか?
Pacerier

10

HashMapを継承する新しいクラスを作成し、getDefaultメソッドを追加するだけです。これがサンプルコードです:

public class DefaultHashMap<K,V> extends HashMap<K,V> {
    public V getDefault(K key, V defaultValue) {
        if (containsKey(key)) {
            return get(key);
        }

        return defaultValue;
    }
}

Ed Staubのコメントで指定された理由とMapインターフェースの契約を破るので、実装でget(K key)メソッドをオーバーライドしないでください(これにより、見つけにくい場合があります)バグ)。


4
getメソッドをオーバーライドしないことにポイントがあります。一方、ソリューションでは、インターフェイスを介してクラスを使用することはできません。これはよくあるケースです。
KrzysztofJabłoński2014年


3

これはデフォルトで行われます。戻りますnull


@ラリー、完全に問題がないHashMapときに、この機能のためだけにサブクラス化するのは少しばかげているようですnull
mrkhrts '09 / 09/22

15
NullObjectただし、パターンを使用している場合や、コード全体にnullチェックを分散させたくない場合は問題があります。
デイブニュートン


1

私はLazyMapが非常に役に立ったと感じました。

get(Object)メソッドがマップに存在しないキーで呼び出されると、ファクトリを使用してオブジェクトが作成されます。作成されたオブジェクトは、要求されたキーを使用してマップに追加されます。

これにより、次のようなことが可能になります。

    Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
    map.get(notExistingKey).incrementAndGet();

の呼び出しgetは、指定されたキーのデフォルト値を作成します。デフォルト値の作成方法は、factory引数を使用して指定しLazyMap.lazyMap(map, factory)ます。上記の例では、マップはAtomicInteger値0の新しいマップに初期化されています。


これは、デフォルト値がファクトリによって作成されるという点で、受け入れられた回答よりも優れています。私のデフォルト値がList<String>-受け入れられた回答を使用している場合はどうですか- (たとえば)ファクトリーからではなく、新しいキーごとに同じリストを使用するリスクがありnew ArrayList<String>()ます。


0
/**
 * Extension of TreeMap to provide default value getter/creator.
 * 
 * NOTE: This class performs no null key or value checking.
 * 
 * @author N David Brown
 *
 * @param <K>   Key type
 * @param <V>   Value type
 */
public abstract class Hash<K, V> extends TreeMap<K, V> {

    private static final long serialVersionUID = 1905150272531272505L;

    /**
     * Same as {@link #get(Object)} but first stores result of
     * {@link #create(Object)} under given key if key doesn't exist.
     * 
     * @param k
     * @return
     */
    public V getOrCreate(final K k) {
        V v = get(k);
        if (v == null) {
            v = create(k);
            put(k, v);
        }
        return v;
    }

    /**
     * Same as {@link #get(Object)} but returns specified default value
     * if key doesn't exist. Note that default value isn't automatically
     * stored under the given key.
     * 
     * @param k
     * @param _default
     * @return
     */
    public V getDefault(final K k, final V _default) {
        V v = get(k);
        return v == null ? _default : v;
    }

    /**
     * Creates a default value for the specified key.
     * 
     * @param k
     * @return
     */
    abstract protected V create(final K k);
}

使用例:

protected class HashList extends Hash<String, ArrayList<String>> {
    private static final long serialVersionUID = 6658900478219817746L;

    @Override
        public ArrayList<Short> create(Short key) {
            return new ArrayList<Short>();
        }
}

final HashList haystack = new HashList();
final String needle = "hide and";
haystack.getOrCreate(needle).add("seek")
System.out.println(haystack.get(needle).get(0));

0

サーバーから返された結果をJSONで読み取る必要があり、フィールドが存在することを保証できませんでした。HashMapから派生したorg.json.simple.JSONObjectクラスを使用しています。ここに私が採用したいくつかのヘルパー関数があります:

public static String getString( final JSONObject response, 
                                final String key ) 
{ return getString( response, key, "" ); }  
public static String getString( final JSONObject response, 
                                final String key, final String defVal ) 
{ return response.containsKey( key ) ? (String)response.get( key ) : defVal; }

public static long getLong( final JSONObject response, 
                            final String key ) 
{ return getLong( response, key, 0 ); } 
public static long getLong( final JSONObject response, 
                            final String key, final long defVal ) 
{ return response.containsKey( key ) ? (long)response.get( key ) : defVal; }

public static float getFloat( final JSONObject response, 
                              final String key ) 
{ return getFloat( response, key, 0.0f ); } 
public static float getFloat( final JSONObject response, 
                              final String key, final float defVal ) 
{ return response.containsKey( key ) ? (float)response.get( key ) : defVal; }

public static List<JSONObject> getList( final JSONObject response, 
                                        final String key ) 
{ return getList( response, key, new ArrayList<JSONObject>() ); }   
public static List<JSONObject> getList( final JSONObject response, 
                                        final String key, final List<JSONObject> defVal ) { 
    try { return response.containsKey( key ) ? (List<JSONObject>) response.get( key ) : defVal; }
    catch( ClassCastException e ) { return defVal; }
}   

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