2012-01-30 7 views
5

これは悪い習慣かもしれませんが、私は自分の問題に対してより良い解決策を見つけ出すことができませんでした。だから、私はこの1つを試してみましたが、私は(当然)マップのマップのマップを適切に遅延させるにはどうすればいいですか?

を動作しませんでしたので、私はこの

properties.get("a").get("b").get("c"); 

NullPointerExceptionを得ることはありません私はこのマップ

// Map<state, Map<transition, Map<property, value>>> 
private Map<String, Map<String, Map<String, String>>> properties; 

を持っていると私はそれを初期化したいです

properties = new HashMap<String, Map<String, Map<String,String>>>(); 

私が試した他のものはコンパイルされませんでした。

また、このネストされたマップを回避する方法があれば、私はそれを感謝します。

+0

*「悪い習慣であるかもしれませんが、私の問題の解決方法はわかりませんでした」*あなたは正しいです。それはほぼ確実に悪い習慣です。問題の概要(具体的にはデータ構造の要件)を書いた新しい質問を書いた場合、誰かが理解できないより良い解決策を提案できるかもしれません。 –

+0

ありがとうございます。私は... – user219882

答えて

5

マップ内のマップにマップを配置する必要があります。文字通り:

properties = new HashMap<String, Map<String, Map<String,String>>>(); 
properties.put("a", new HashMap<String, Map<String,String>>()); 
properites.get("a").put("b", new HashMap<String,String>()); 

あなたの目標は、あなたがあなた自身のマップを作成する必要がNPEずに遅延初期化の場合:

private static abstract class MyMap<K, V> extends HashMap<K, V> { 
    @Override 
    public V get(Object key) { 
     V val = super.get(key); 
     if (val == null && key instanceof K) { 
      put((K)key, val = create()); 
     } 
     return val; 
    } 

    protected abstract V create(); 
} 


public void initialize() { 
    properties = new MyMap<String, Map<String, Map<String, String>>>() { 
     @Override 
     protected Map<String, Map<String, String>> create() { 
      return new MyMap<String, Map<String, String>>() { 
       @Override 
       protected Map<String, String> create() { 
        return new HashMap<String, String>(); 
       } 
      }; 
     } 
    }; 

} 
+0

しかし、私は最初にそこに任意の値を入れたくありません。つまり、ここでの唯一の解決策は、アクセス中にnullをチェックし、そうであれば次のレベルを作成することです。 – user219882

+0

私は自分の答えを更新しました。 –

+0

ありがとうございました – user219882

1

この構造で実行する唯一の方法は、可能なすべてのキーで第1レベルマップと第2レベルマップを事前に初期化することです。これができない場合は、プレーンな地図で何を求めているのかを達成できません。

代わりに、より寛容なカスタムデータ構造を構築できます。たとえば、一般的なやり方は、失敗したキー・ルックアップがヌルではなく「空の」構造体を戻し、ネストされたアクセスを可能にすることです。

8

あなたがあなた自身のKeyクラスを作成する必要があるように私には思える:

public class Key { 
    private final String a; 
    private final String b; 
    private final String c; 
    public Key(String a, String b, String c) { 
     // initialize all fields here 
    } 

    // you need to implement equals and hashcode. Eclipse and IntelliJ can do that for you 
} 

独自のキークラスを実装すると、マップは次のようになります。

Map<Key, String> map = new HashMap<Key, String>(); 

そしてマップで何かを探しているときに使用できます。

map.get(new Key("a", "b", "c")); 

方法は、上記NullPointerExceptionがスローされません。

このソリューションが機能するには、Keyクラスのequalsとhashcodeをオーバーライドする必要があります。助けがありますhere。 equalsとhashcodeをオーバーライドしないと、同じ要素を持つ新しいキーはマップの既存のキーと一致しません。

他にも解決策がありますが、独自のキーを実装するのは私の意見ではきれいです。あなたはコンストラクタを使用したくない場合は、静的メソッドを使用してキーを初期化し、のようなものを使用することができます:それはあなた次第です

Key.build(a, b, c) 

を。

+1

+1。私は、ジェネリックペアとトリプルコンテナクラス(それぞれ2つまたは3つのオブジェクトを保持する)がこの目的のために有用であることを発見しました。 – ach

1

これを一度に初期化することはできません。通常、どのキーを事前に持っているのかわからないからです。

したがって、キーのサブマップがヌルであるかどうかを確認しなければならない場合は、に空のマップを追加してください。好ましくは、マップにエントリを追加するときにのみ行い、エントリを取得すると、パス内のサブマップの1つが存在しない場合はnullを返します。使いやすさのためにマップ実装でそれをラップすることができます。

代わりに、apache commons collections 'MultiKeyMapが必要なものを提供する可能性があります。

1

properties.get("a").get("b").get("c");を使用することは不可能であり、独自のマップを作成しない限り、nullを避けるようにしてください。実際には、マップに "b"キーが含まれると予測することはできません。 ネストされたgetを処理する独自のクラスを作成しようとします。

1

もっと良い解決策は、値のマップの唯一のキーとしてオブジェクトを使用していると思います。キーは、statetransitionpropertyの3つのフィールドで構成されます。

import org.apache.commons.lang3.builder.EqualsBuilder; 
import org.apache.commons.lang3.builder.HashCodeBuilder; 

public class Key { 

    private String state; 

    private String transition; 

    private String property; 

    public Key(String state, String transition, String property) { 
     this.state = state; 
     this.transition = transition; 
     this.property = property; 
    } 

    @Override 
    public boolean equals(Object other) { 
     return EqualsBuilder.reflectionEquals(this, other); 
    } 

    @Override 
    public int hashCode() { 
     return HashCodeBuilder.reflectionHashCode(this); 
    } 

} 

あなたが値をチェックすると、マップが値

Map<Key, String> values = new HashMap<Key, String>(); 
assert values.get(new Key("a", "b", "c")) == null; 

values.put(new Key("a", "b", "c"), "value"); 
assert values.get(new Key("a", "b", "c")) != null; 
assert values.get(new Key("a", "b", "c")).equals("value"); 

効率的かつ正確にあなたMapでキーとしてオブジェクトを使用するに関連付けられていないキーのnullを返しますがメソッドequals()hashCode()をオーバーライドする必要があります。私はCommons Langライブラリの反射機能を使ってメソッドを構築しました。

+0

答えはOKですが、適切な(リフレクションベースではない)等価が必要です()&hashCode()の実装です。リフレクションベースのものはどちらも演出的ではありません。マップキーの場合はこれが重要です。また、それを正しく行うための基本原則も示していません。 –

5

あなたはユーティリティメソッドを使用できます:それはタイプセーフではないとしてこれを使用する際には注意が必要です

String result = get(properties, "a", "b", "c"); 

注:これは、このように呼び出すことができ

public static <T> T get(Map<?, ?> properties, Object... keys) { 
    Map<?, ?> nestedMap = properties; 
    for (int i = 0; i < keys.length; i++) { 
     if (i == keys.length - 1) { 
     @SuppressWarnings("unchecked") 
     T value = (T) nestedMap.get(keys[i]); 
     return value; 
     } else { 
     nestedMap = (Map<?, ?>) nestedMap.get(keys[i]); 
     if(nestedMap == null) { 
      return null; 
     } 
     } 
    } 
    return null; 
    } 

を。

関連する問題