2012-07-28 8 views
22

ハッシュマップにオブジェクトのグループを格納します。ここで、キーは2つの文字列値の複合でなければなりません。これを達成する方法はありますか?Java:ハッシュマップのコンポジットキー

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

答えて

37

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

問題がある、あなたは平等テストと二つのそのようなオブジェクトのハッシュコードを決定する必要があります。

(これは議論の余地がある)平等は両方の文字列に一致することができ、ハッシュコードは、連結部材のハッシュコードとすることができる:

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(); 
    } 
} 
+1

ABC、D、ABのハッシュコードによる問題はありますか?CDは同じですか?それとも、等しく異なるのですか? –

+1

@smackfu:それは異なります。このような文字列のペアが多い場合は、テーブル内の同じスロットにハッシュしてルックアップの効率が悪くなるため、問題が発生します。 – Tudor

+0

@Tudor基本的にちょうどチルダ文字で区切られた2つの文字列を連結するEdgeCaseによって提示されるソリューションに対して、このソリューションが持つ利点を考えてみてください。 – Zak

7

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()忘れてはいけません。不変性要件の背景を含む、HashMapsとキーの詳細については、this blog entryを参照してください。あなたのキーが不変でない場合は、そのコンポーネントを変更することができ、その後の検索ではそれを見つけることができません(Stringのような不変オブジェクトがキーの良い候補です)

そうですよね、理想ではない。状況によってはうまくいくことがありますが、しばしば信頼性が低く壊れやすいソリューションです(例:AB/CA/BCとは異なるキーですか)。あなたは2つの文字列を含むカスタムオブジェクトを持つことができ

+0

多くのエントリ(〜77,500)が必要な場合、ハッシュの衝突で自分自身を見つけることができますか? – lolo

4

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

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

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

それは簡単ですが、それは動作します。

1
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");
OUTPUT: keyPart1~keyPart2~keyPart3
9
public int hashCode() { 
    return (str1 + str2).hashCode(); 
} 

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

How do I calculate a good hash code for a list of strings?

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(); 

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

+0

"ハッシュコードが計算されるたびに新しい文字列インスタンス" - ハハハ、よく目撃された! –

2

多くの人がネストマップを使用しています。つまり、Key1 -> Key2 -> Value(コンピュータサイエンス/別名2つの引数を持ち、値を生成する(Key1 x Key2) -> Valueマッピングのhaskell curring表記を使用します)、最初に最初のキーを指定します。これにより、(partial) mapKey2 -> 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) 
} 

私はそれは、プレーン複合キー建設よりも良いかではないことを確認していません。あなたはそれについてコメントするかもしれません。

2

spaguetti/cactusのスタックについて私は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

ホイールを改造する必要はありません。あなたの必要性のためにTable<R,C,V>インタフェースのGuavaHashBasedTable<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 

ハッピーコーディング!

関連する問題