6

次のようなマップを実装しようとしています

Map<<key1, key2>, List<value>>

マップには 2 つのキーが含まれている必要があり、対応する値はリストになります。少なくとも 1 つのキー値が等しい場合、同じリストにレコードを追加したい たとえば、次のレコードを考えてみましょう

R1[key1, key2]
R2[key1, null/empty] - Key1 is equal
R3[null/empty, key2] - Key2 is equal
R4[key1, key2] - Key1 and Key2 both are equal.

すべてを同じリストに挿入する必要があります

Key = <Key1,Key2> 
Value = <R1, R2, R3, R4>

Guava テーブルまたはcommons MulitKeyMapを使用できません(これだけのためにライブラリ全体を含めたくない)。

key1私は属性として と の両方を持つクラス (キーとして使用できる) を実装しようとしましたがkey2、key1 と key2 を考慮しない効果的なハッシュコードの実装は少し (または多くの場合) トリッキーに見えます

public class Key {
    private int key1;
    private int key2;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        // Cant include key1 and key2 in hashcode 
       /* result = prime * result + key1;
        result = prime * result + key2;*/
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Key other = (Key) obj;
        if(key2 and other.key2 both not blank/null){ // pseudo code
        if (key2 == other.key2)
            return true;
        }
        if(key1 and other.key1 both not blank/null){ //pseudo code
        if (key1 == other.key1)
            return true;
        }
        return true;
    }

}

すべてに同じハッシュコードを使用しても機能しますが、数千のレコードがあるため、パフォーマンスに影響します。


編集:
ネストされたマップを使用することはできませ

Map<key1, Map< key2, List<value>>>

一部のレコードにはキーが 1 つしかない場合があります。

  R1[key1, key2]     - Have both keys
  R2[key1, null/empty] - Key1 is equal
  R3[null/empty, key2] - Key1 is missing and key2 is equal

ここで、R3 には key1 がないため、R1 および R2 と同じ場所に挿入することはできません


編集2:

私も介入命令を維持したいと思います。

4

9 に答える 9

4

定義上、マップには値ごとに 1 つのキーがあります。

マップのマップを使用することも、キーを 2 つのフィールドを持つオブジェクトにすることもできますが、それは可能な限り近いものです。

マップのマップ:

Map myMap<key, Map<otherkey, value>>

カスタム オブジェクト

public class MapKey {
    public Object keyFirstPart;
    public Object keySecondPart;

    // You'll need to implement equals, hashcode, etc
}

Map myyMap <MapKey, value>
于 2013-10-04T08:49:12.613 に答える
1

HashMap のような動作が必要な場合は、2 つのマップを作成し、魔法のようにコレクションを処理します (また、これには Sets を使用することをお勧めします...)。

public class MyMap<K1, K2, V> {
  Map<K1, Collection<V>> map1;
  Map<K2, Collection<V>> map2;

  //have to add to both lists
  put(K1 k1, K2 k2, V v) {
     addToCollection(map1, k1, v);
     addToCollection(map2, k2, v);
  }

  //notice T param
  <T> void addToCollection(Map<T, Collection<V>> map, T key, V value ) {
     Collection<V> collection= map.get(key);
     if(collection==null) {
       collection= new HashSet<V>();
       map.put(key, collection);
     }
     collection.add(value );
  }

  public Collection<V> get(K1 k1, K2 k2) {
     Collection<V> toReturn = new HashSet<V>();
     Collection<V> coll1 = map1.get(k1);
     if(coll1!=null) {
        toReturn.addAll(coll1);
     }

     Collection<V> coll2 = map2.get(k2);
     if(coll2!=null) {
        toReturn.addAll(coll2);
     }

     return toReturn;
  }
}
于 2013-10-04T09:21:10.957 に答える
1

これを試して....

マップへのキーのクラスを作成します

public class MapKey {
private Object key1;
private Object key2;

@Override
public boolean equals(Object object) {
    boolean equals = false;
    if (((MapKey) object).key1 == null && ((MapKey) object).key2 == null) {
        equals = true;
    }
    if (((MapKey) object).key1.equals(this.key1) && ((MapKey) object).key2.equals(this.key2)) {
        equals = true;
    }
    if (((MapKey) object).key1 == null && ((MapKey) object).key2.equals(this.key2)) {
        equals = true;
    }
    if (((MapKey) object).key1.equals(this.key1) && ((MapKey) object).key2 == null) {
        equals = true;
    }
    return equals;

}

@Override
public int hashCode() {
    return 1;
}

public Object getKey1() {
    return key1;
}

public void setKey1(Object key1) {
    this.key1 = key1;
}

public Object getKey2() {
    return key2;
}

public void setKey2(Object key2) {
    this.key2 = key2;
}
}

上記のクラスでは、必要に応じて key1 と key2 の DataTypes を変更できます。必要なロジックを実行するメイン クラスは次のとおりです。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MapWithTwoKeys {
private static final Map<MapKey, List<Object>> mapWithTwoKeys = new HashMap<MapKey,     List<Object>>();

public static void main(String[] args) {

// Create first map entry with key <A,B>.
MapKey mapKey1 = new MapKey();
mapKey1.setKey1("A");
mapKey1.setKey2("B");

List<Object> list1 = new ArrayList<Object>();
list1.add("List1 Entry");

put(mapKey1, list1);

// Create second map entry with key <A,B>, append value.
MapKey mapKey2 = new MapKey();
mapKey2.setKey1("A");
mapKey2.setKey2("B");

List<Object> list2 = new ArrayList<Object>();
list2.add("List2 Entry");

put(mapKey2, list2);

// Create third map entry with key <A,>.
MapKey mapKey3 = new MapKey();
mapKey3.setKey1("A");
mapKey3.setKey2("");

List<Object> list3 = new ArrayList<Object>();
list3.add("List3 Entry");

put(mapKey3, list3);

// Create forth map entry with key <,>.
MapKey mapKey4 = new MapKey();
mapKey4.setKey1("");
mapKey4.setKey2("");

List<Object> list4 = new ArrayList<Object>();
list4.add("List4 Entry");

put(mapKey4, list4);

// Create forth map entry with key <,B>.
MapKey mapKey5 = new MapKey();
mapKey5.setKey1("");
mapKey5.setKey2("B");

List<Object> list5 = new ArrayList<Object>();
list5.add("List5 Entry");

put(mapKey5, list5);

for (Map.Entry<MapKey, List<Object>> entry : mapWithTwoKeys.entrySet()) {
System.out.println("MapKey Key: <" + entry.getKey().getKey1() + ","
        + entry.getKey().getKey2() + ">");
System.out.println("Value: " + entry.getValue());
System.out.println("---------------------------------------");
}
}

/**
 * Custom put method for the map.
 * @param mapKey2 (MapKey... the key object of the Map).
 * @param list (List of Object... the value of the Map).
*/
private static void put(MapKey mapKey2, List<Object> list) {
if (mapWithTwoKeys.get(mapKey2) == null) {
    mapWithTwoKeys.put(mapKey2, new ArrayList<Object>());
}
mapWithTwoKeys.get(mapKey2).add(list);
}
}

コードは非常に単純明快で、理解しやすいものです。これがあなたの要件を満たしているかどうか教えてください。

于 2013-10-04T17:50:52.033 に答える
1

代わりに TreeMap を使用してください。このようにして、Hashcode の代わりに CustomKey クラスのカスタム コンパレータを使用できます。

TreeMap<CustomKey, List<value>> map = new TreeMap<CustomKey, List<value>>(myComparator);

eta: コンパレータ クラスを作成する代わりに、CustomKey クラスに Comparable を実装させることができます。

于 2013-10-04T09:07:17.700 に答える
0

key1 のハッシュ コードのみをチェックし、一致する場合は、とにかく equals メソッドでテストします。

@Override
    public int hashCode() {
        return key1.hashCode();
    }

@Override
public boolean equals(Object obj) {
Key k = (Key)obj;
// Generic equals code goes here
    if(this.key1.equals(k.key1) && this.key2.equals(k.key2) )
        return true;
    return false;
}
于 2013-10-04T09:02:56.180 に答える
0

基本的にこれを達成するためのアイデアは、値自体によってキーをマップすることです。

したがって、これを行う内部マップを持つことができます (ここでは、2 つだけではなく一連のキーがあります)。

Map<V, Set<K>> keySetMap = new HashMap<V, Set<K>>();

したがって、Map実装は次のようになります。

public class MultiKeyMap<K, V> extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 1L;

    private Map<V, Set<K>> keySetMap = new HashMap<V, Set<K>>();

    @Override
    public V put(K key, V value) {
        V v = null;

        Set<K> keySet = keySetMap.get(value);
        if(keySet == null) {
            keySet = new LinkedHashSet<K>();
            keySetMap.put(value, keySet);
        }

        keySet.add(key);
        v = super.put(key, value);

        // update the old keys to reference the new value
        Set<K> oldKeySet =  keySetMap.get(v);
        if(oldKeySet != null) {
            for(K k : oldKeySet) {
                super.put(k, value);
            }
        }

        return v;
    }
}

これは、単純な (不変の) オブジェクトでは問題なく機能します。

@Test
public void multiKeyMapString() {
    MultiKeyMap<String, String> m = new MultiKeyMap<String, String>();

    m.put("1", "A");
    m.put("2", "B");

    for(Entry<String, String> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    m.put("3", "A");

    System.out.println("----");
    for(Entry<String, String> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    m.put("4", "C");

    System.out.println("----");
    for(Entry<String, String> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    m.put("3", "D");

    System.out.println("----");
    for(Entry<String, String> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    System.out.println("----");
    System.out.println("values=" + m.values());

    System.out.println();
    System.out.println();
}

上記のテストでは、出力は次のようになります

K=1, V=A
K=2, V=B
----
K=1, V=A
K=2, V=B
K=3, V=A
----
K=1, V=A
K=2, V=B
K=3, V=A
K=4, V=C
----
K=1, V=D
K=2, V=B
K=3, V=D
K=4, V=C
----
values=[D, B, C]

最後の出力でわかるように、以前に によってマップされた値が前のステップでによってマップされたものと同じであるため、キー1は値をマップするようになりました。D31

しかし、リスト (または任意の変更可能なオブジェクト) をマップに配置する場合は注意が必要です。リストを変更 (要素の追加/削除) すると、リストにはhashCode以前のキーのマッピングに使用された別のリストが含まれるためです。

@Test
public void multiKeyMapList() {
    List<String> l = new ArrayList<String>();
    l.add("foo");
    l.add("bar");
    MultiKeyMap<String, List<String>> m = new MultiKeyMap<String, List<String>>();

    m.put("1", l);
    m.put("2", l);

    for(Entry<String, List<String>> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    m.get("1").add("foobar");
    m.put("3", l);

    System.out.println("----");
    for(Entry<String, List<String>> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    l = new ArrayList<String>();
    l.add("bla");

    m.put("4", l);

    System.out.println("----");
    for(Entry<String, List<String>> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    m.put("3", l);

    System.out.println("----");
    for(Entry<String, List<String>> e : m.entrySet()) {
        System.out.println("K=" + e.getKey() + ", V=" + e.getValue().toString());
    }

    System.out.println("----");
    System.out.println("values=" + m.values());
}

上記のテストでは、次のような出力が得られます。

K=1, V=[foo, bar]
K=2, V=[foo, bar]
----
K=1, V=[foo, bar, foobar]
K=2, V=[foo, bar, foobar]
K=3, V=[foo, bar, foobar]
----
K=1, V=[foo, bar, foobar]
K=2, V=[foo, bar, foobar]
K=3, V=[foo, bar, foobar]
K=4, V=[bla]
----
K=1, V=[foo, bar, foobar]
K=2, V=[foo, bar, foobar]
K=3, V=[bla]
K=4, V=[bla]
----
values=[[foo, bar, foobar], [bla]]

によってマップされた値が更新されていないことがわかるように、キーのみが1別の値をマップするようになった後。その理由は、結果が正しい結果を返さない原因とは異なるためです。これを克服するには、実際の値と比較してキーのセットを取得する必要があります。23hashCode[foo, bar][foo, bar, foobar]Map#get

public class MultiKeyMap<K, V> extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 1L;

    private Map<V, Set<K>> keySetMap = new HashMap<V, Set<K>>();

    @Override
    public V put(K key, V value) {
        V v = null;

        Set<K> keySet = keySetMap.get(value);
        if (keySet == null) {
            keySet = new LinkedHashSet<K>();
            keySetMap.put(value, keySet);
        }

        keySet.add(key);
        v = super.put(key, value);

        // update the old keys to reference the new value
        for (K k : getKeySetByValue(v)) {
            super.put(k, value);
        }

        return v;
    }

    @Override
    public Collection<V> values() {
        // distinct values
        return new LinkedHashSet<V>(super.values());
    }

    private Set<K> getKeySetByValue(V v) {
        Set<K> set = null;
        if (v != null) {
            for (Map.Entry<V, Set<K>> e : keySetMap.entrySet()) {
                if (v.equals(e.getKey())) {
                    set = e.getValue();
                    break;
                }
            }
        }
        return set == null ? Collections.<K> emptySet() : set;
    }
}

両方のテストを再度実行すると、次の出力が得られます。

単純な (不変の) オブジェクトの場合

K=1, V=A
K=2, V=B
----
K=1, V=A
K=2, V=B
K=3, V=A
----
K=1, V=A
K=2, V=B
K=3, V=A
K=4, V=C
----
K=1, V=D
K=2, V=B
K=3, V=D
K=4, V=C
----
values=[D, B, C]

変更可能なオブジェクトの場合

K=1, V=[foo, bar]
K=2, V=[foo, bar]
----
K=1, V=[foo, bar, foobar]
K=2, V=[foo, bar, foobar]
K=3, V=[foo, bar, foobar]
----
K=1, V=[foo, bar, foobar]
K=2, V=[foo, bar, foobar]
K=3, V=[foo, bar, foobar]
K=4, V=[bla]
----
K=1, V=[bla]
K=2, V=[bla]
K=3, V=[bla]
K=4, V=[bla]
----
values=[[bla]]

これが、マップを実装する方法を見つけるのに役立つことを願っています。既存の実装を拡張する代わりに、Mapインターフェイスを実装して、コントラクトに関してすべてのメソッドの実装を提供し、実際のマッピングを処理するメンバーとして選択した実装を持たせることができます。

于 2013-10-04T13:11:08.773 に答える
-1

Map関係 key->intermediateKey を保持する別のものを作成します。中間キーは、自動的に生成され、一意であることが保証されている GUID またはその他のものである場合があります。

  Map<String, GUID> first = new HashMap<String, GUID>();
  first.put(key1, guid1);
  first.put(key2, guid1);

  Map<GUID, ValueType> second = new HashMap<GUID, ValueType>();
  second.put(guid1, value1);

別の方法として (ただし、はるかに複雑で柔軟性が低いと思いますが)、キーで遊ぶこともできます。If key1.equals(key2)(そして、その結果、key2.equals(key1)&& (key1.hashCode() == key2.hashCode ) thenMap.get(key1) will return the same value thanMap.get(key2)`.

于 2013-10-04T08:51:15.360 に答える