web-dev-qa-db-ja.com

Java:ハッシュマップの複合キー

ハッシュマップにオブジェクトのグループを保存したいのですが、キーは2つの文字列値の複合です。これを達成する方法はありますか?

私は単に2つの文字列を連結することができますが、これを行うにはより良い方法があると確信しています。

24
user1203861

次の2つの文字列を含むカスタムオブジェクトを作成できます。

class StringKey {
    private String str1;
    private String str2;
}

問題は、このような2つのオブジェクトの等価性テストとハッシュコードを決定する必要があることです。

平等は両方の文字列の一致であり、ハッシュコードは連結されたメンバーのハッシュコードである可能性があります(これは議論の余地があります)。

class StringKey {
    private String str1;
    private String str2;

    @Override
    public boolean equals(Object obj) {
        if(obj != null && obj instanceof StringKey) {
            StringKey s = (StringKey)obj;
            return str1.equals(s.str1) && str2.equals(s.str2);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (str1 + str2).hashCode();
    }
}
40
Tudor
public int hashCode() {
    return (str1 + str2).hashCode();
}

これはhashCodeを生成するひどい方法のようです:ハッシュコードが計算されるたびに新しい文字列インスタンスを作成するのはひどいです! (一度文字列インスタンスを生成し、その結果をキャッシュすることは悪い習慣です。)

ここには多くの提案があります:

文字列のリストに対して適切なハッシュコードを計算する方法

public int hashCode() {
    final int prime = 31;
    int result = 1;
    for ( String s : strings ) {
        result = result * prime + s.hashCode();
    }
    return result;
}

文字列のペアの場合、次のようになります。

return string1.hashCode() * 31 + string2.hashCode();

これは非常に基本的な実装です。より適切に調整された戦略を提案するためのリンクを介した多くのアドバイス。

10
Thomas Bitonti

なぜ2つの文字列をメンバーとして含む(たとえば)Pairオブジェクトを作成し、これをキーとして使用しないのですか?

例えば.

public class Pair {
   private final String str1;
   private final String str2;

   // this object should be immutable to reliably perform subsequent lookups
}

equals() および hashCode() を忘れないでください。 HashMapとキーの詳細(不変性要件の背景を含む)については、 このブログエントリ を参照してください。キーが不変ではない場合、そのコンポーネントを変更でき、その後のルックアップはそれを見つけることができません(これがStringなどの不変オブジェクトがキーの良い候補である理由です)

連結は理想的ではないというのは正しいことです。状況によっては機能しますが、多くの場合、信頼性が低く脆弱なソリューションです(例:AB/Cとは異なるキーA/BC?).

7
Brian Agnew

同様のケースがあります。私がすることは、チルダ(〜)で区切られた2つの文字列を連結することです。

したがって、クライアントがサービス関数を呼び出してマップからオブジェクトを取得すると、次のようになります。

MyObject getMyObject(String key1, String key2) {
    String cacheKey = key1 + "~" + key2;
    return map.get(cachekey);
}

簡単ですが、機能します。

5
EdgeCase

車輪を再発明する必要はありません。必要に応じて、 GuavaHashBasedTable<R,C,V>インターフェースのTable<R,C,V>実装を使用するだけです。ここに例があります

Table<String, String, Integer> table = HashBasedTable.create();

table.put("key-1", "lock-1", 50);
table.put("lock-1", "key-1", 100);

System.out.println(table.get("key-1", "lock-1")); //prints 50
System.out.println(table.get("lock-1", "key-1")); //prints 100

table.put("key-1", "lock-1", 150); //replaces 50 with 150

ハッピーコーディング!

4
TMtech

多くの人がネストされたマップを使用していることがわかります。つまり、Key1 -> Key2 -> Value(私はコンピューターサイエンスを使用します/別名haskellカリング表記法(Key1 x Key2) -> Valueマッピングは2つの引数を持ち、値を生成します)、最初のキーを最初に指定します-これは、 (部分)マップKey2 -> Value、次のステップで展開します。

例えば、

Map<File, Map<Integer, String>> table = new HashMap(); // maps (File, Int) -> Distance

add(k1, k2, value) {
  table2 = table1.get(k1);
  if (table2 == null) table2 = table1.add(k1, new HashMap())
  table2.add(k2, value)
}

get(k1, k2) {
  table2 = table1.get(k1);
  return table2.get(k2)
}

単純な複合キー構造よりも優れているかどうかはわかりません。あなたはそれについてコメントすることができます。

3
Val

スパゲッティ/サボテンスタックについて読んで、map.lookup( "a"、 "b")およびmap.lookup( "のようにキーを任意の順序でマッピングする可能性を含む、この目的に役立つ可能性のあるバリアントを思い付きました。 b "、" a ")は同じ要素を返します。また、2つだけでなく、任意の数のキーで機能します。

私はそれをデータフロープログラミングの実験用のスタックとして使用しますが、ここではマルチキーマップとして機能するクイックでダーティなバージョンです(改善する必要があります:配列の代わりにセットを使用して、キーの重複した出現を検索しないでください)

public class MultiKeyMap <K,E> {
    class Mapping {
        E element;
        int numKeys;
        public Mapping(E element,int numKeys){
            this.element = element;
            this.numKeys = numKeys;
        }
    }
    class KeySlot{
        Mapping parent;
        public KeySlot(Mapping mapping) {
            parent = mapping;
        }
    }
    class KeySlotList extends LinkedList<KeySlot>{}
    class MultiMap extends HashMap<K,KeySlotList>{}
    class MappingTrackMap extends HashMap<Mapping,Integer>{}

    MultiMap map = new MultiMap();

    public void put(E element, K ...keys){
        Mapping mapping = new Mapping(element,keys.length);
        for(int i=0;i<keys.length;i++){
            KeySlot k = new KeySlot(mapping);
            KeySlotList l = map.get(keys[i]);
            if(l==null){
                l = new KeySlotList();
                map.put(keys[i], l);
            }
            l.add(k);
        }
    }
    public E lookup(K ...keys){
        MappingTrackMap tmp  = new MappingTrackMap();
        for(K key:keys){
            KeySlotList l = map.get(key);
            if(l==null)return null;
            for(KeySlot keySlot:l){
                Mapping parent = keySlot.parent;
                Integer count = tmp.get(parent);
                if(parent.numKeys!=keys.length)continue;
                if(count == null){
                    count = parent.numKeys-1;
                }else{
                    count--;
                }
                if(count == 0){
                    return parent.element;
                }else{
                    tmp.put(parent, count);
                }               
            }
        }
        return null;
    }
    public static void main(String[] args) {
        MultiKeyMap<String,String> m = new MultiKeyMap<String,String>();
        m.put("brazil", "yellow", "green");
        m.put("canada", "red", "white");
        m.put("USA", "red" ,"white" ,"blue");
        m.put("argentina", "white","blue");

        System.out.println(m.lookup("red","white"));  // canada
        System.out.println(m.lookup("white","red"));  // canada
        System.out.println(m.lookup("white","red","blue")); // USA
    }
}
2
Rupert Hide
public static String fakeMapKey(final String... arrayKey) {
    String[] keys = arrayKey;

    if (keys == null || keys.length == 0)
        return null;

    if (keys.length == 1)
        return keys[0];

    String key = "";
    for (int i = 0; i < keys.length; i++)
        key += "{" + i + "}" + (i == keys.length - 1 ? "" : "{" + keys.length + "}");

    keys = Arrays.copyOf(keys, keys.length + 1);

    keys[keys.length - 1] = FAKE_KEY_SEPARATOR;

    return  MessageFormat.format(key, (Object[]) keys);}
public static string FAKE_KEY_SEPARATOR = "〜"; 

INPUT: fakeMapKey( "keyPart1"、 "keyPart2"、 "keyPart3");
出力:keyPart1〜keyPart2〜keyPart3
1
Nelson Azevedo