多键映射

6

我正在尝试实现一个类似地图的功能

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

地图应包含2个键,相应的值将是一个列表。如果至少有一个键的值相等,则我想在同一列表中添加记录。 例如,考虑以下记录

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 tablecommons MulitKeyMap(只是为了这个不想包含整个库)。
我试图实现一个类(可以用作键),它将具有key1key2作为属性,但实现一个有效的哈希码而不考虑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,例如

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

由于某些记录可能只有一个键。

  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:

我也希望保持插入顺序。


你可以使用不同的键将列表添加两次吗?因为你只是将列表的引用存储为值。如果我错了,请纠正我。 - Ortwin Angermeier
@ortang:在一个列表中吗? - Ajinkya
是的,一个列表,你的值是一个列表。因此,您有一个单独的列表,可以将其两次(如果需要,甚至更多次)添加到Map中,使用不同的键。由于添加的值只是对列表的引用,因此这当然只适用于每个键都是唯一的情况。如果我正确理解了您的问题,那么它应该可以工作。 - Ortwin Angermeier
1
如果我对相同的记录进行多次插入,那么在处理后我必须删除重复项。 - Ajinkya
我猜我不理解你的问题。你提到如果至少有一个键值相等(存在于Map中),则要将记录添加到同一列表中。因此,你可以使用类似这样的语句:List<Record> records = map.get(key1) != null ? map.get(key1) : map.get(key2);,然后if(records != null) records.add(record);。也许我在某些地方错过了什么,但是这样应该可以解决问题。 - Ortwin Angermeier
@ortang:它会添加重复记录,因为记录可以具有两个键。 - Ajinkya
9个回答

4

根据定义,每个值在地图中有一个键。

您可以拥有一个地图的地图,或者您的键可以是具有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>

它不会在所有情况下都起作用。请检查我的编辑。 - Ajinkya
gordonM 提出的两个关键解决方案正是您所需要的。我过去也曾不得不这样做,并总是咒骂 Java 没有内置的“元组”类。 - tom
1
@tom:是的,但只有在每个记录都保证有两个键的情况下才能正常工作。 - Ajinkya
@Gordon 你应该再次查看 OP 的问题。这种方法存在一些问题。 - Rohit Jain
所有的地图都是基于哈希的吗?那TreeMap呢?它使用compareTo,您可以在构造函数中传递一个Comparable。 - tom
我认为OP想要的任何结构基本上都不是一个映射。就像我说的,按定义,每个存储引用只有一个键。如果OP想要的行为不是这样的话,那么它就不是一个映射。在给定的示例中,他希望通过最多3种不同的方式访问集合中的条目(一个键或另一个匹配,或两个都匹配)。 - GordonM

2
如果需要类似HashMap的行为,我会创建两个Maps,并在处理集合时进行操作(此外,我建议使用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;
  }
}

+1。看起来非常不错。但是我的整个数据将被分成两个列表,这将导致重复。 - Ajinkya
它不会导致重复,只会有重复的引用。虽然这可能是一个问题,但也许不像重复的实例那么严重... - ppeterka
我的意思是,在所有操作完成后,我需要重新收集数据。然后,我必须合并两个列表,删除重复引用,这样顺序就完全消失了。 - Ajinkya
啊,我明白了。是的,这个实现存在问题。我没有想到值的顺序会很重要...我认为可能可以将这个想法适应到那个要求上,但一定要小心谨慎地进行... - ppeterka

1
使用TreeMap代替,这样你可以使用CustomKey类的自定义比较器而不是哈希码。
TreeMap<CustomKey, List<value>> map = new TreeMap<CustomKey, List<value>>(myComparator);

注:不需要创建比较器类,您可以使CustomKey类实现Comparable接口


但是看起来它不会保持插入顺序。 - Ajinkya
不,它会进行排序。我没有意识到那是一个要求。 - tom
同时对每个元素进行排序会增加很多开销。 - Ajinkya
它在插入时执行,当它调用compareTo方法时。它必须调用compareTo方法,因此开销非常小。 - tom
你需要自己跟踪插入顺序。 在 compareTo 中,如果它们相同,返回0,否则比较插入顺序(可以是在CustomKey类中创建的基于时间的ID戳)。这将按照所需的顺序进行排序,并解决两个键有时为空的插入问题。 - tom

1

请尝试以下操作...

创建一个与Map相关的键的类

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的数据类型。这是执行所需逻辑的主要类。
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);
}
}

这段代码非常直观简单易懂。如果您对此满意,请告诉我。


在处理成千上万条记录时,使用常量哈希码不是一个好主意。 - Ajinkya
是的,常量并不是一个好主意,但这里的意图是绕过哈希码检查并始终进入equals方法。 - ASD
有些记录可能只有一个键,因此哈希码将与具有两个键的记录不同。因此它将无法工作。 - Ajinkya
请给我一个例子,让我知道这个方法不适用的情况,这样我们才能达成共识。 - ASD
R1[key1, key2]R2[key1, null/empty]都将具有不同的哈希码。 - Ajinkya
显示剩余3条评论

1
我认为以下解决方案适合您 - 我使用了MyKey.java对象作为HashMap的键。它包含了两个键和哈希码。哈希码将用于识别您在问题中列出的不同键组合的值列表。当您第一次注册这两个键时,会生成此哈希码。它存储在每个键旁,因此即使其中一个键为空,您也将获得相同的哈希码。

MultiKeyMap.java =>扩展HashMap并覆盖'put'和'get'方法。populateHashKey() - 此方法将为您想要的不同键组合生成/返回相同的哈希码。

注意:插入顺序由Arraylist维护。还有,每个键组合的所有值都将存储在Map的同一个List中。

package test.map;

public class MyKey {

    private String myKey1;
    private String myKey2;
    private int hashKey;

    public MyKey(String key1, String key2) {
       this.myKey1 = key1;
       this.myKey2 = key2;
    }
    /**
     * @return the myKey1
     */
    public String getMyKey1() {
        return this.myKey1;
    }
    /**
     * @param tmpMyKey1 the myKey1 to set
     */
    public void setMyKey1(String tmpMyKey1) {
        this.myKey1 = tmpMyKey1;
    }
    /**
     * @return the myKey2
     */
    public String getMyKey2() {
        return this.myKey2;
    }
    /**
     * @param tmpMyKey2 the myKey2 to set
     */
    public void setMyKey2(String tmpMyKey2) {
        this.myKey2 = tmpMyKey2;
    }
    /**
     * Returns the hash key.
     */
    @Override
    public int hashCode() {
        return this.hashKey;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MyKey other = (MyKey) obj;
        if(checkEqual(this.myKey1, other.myKey1) 
                || checkEqual(this.myKey2, other.myKey2)) {
            return true;
        }

        return false;
    }
    /*
     * Checks whether key1 equals key2.
     */
    private boolean checkEqual(String key1, String key2) {
        if(key1 != null && key2 != null) {
            return key1.equals(key2);
        }
        return false;
    }
    /**
     * @return the hashKey
     */
    public int getHashKey() {
        return this.hashKey;
    }
    /**
     * @param tmpHashKey the hashKey to set
     */
    public void setHashKey(int tmpHashKey) {
        this.hashKey = tmpHashKey;
    }
}

MultiKeyMap.java - 多键映射。
  package test.map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MultiKeyMap extends HashMap<MyKey, List<String>>{

    private static final long serialVersionUID = 3523468186955908397L;
    Map<String, Integer> hashKeyMap = new HashMap<String, Integer>();

    /*
     * Adds single value in the List of values against the key
     */
    public List<String> addValue(MyKey tmpKey, String tmpValue) {
        populateHashKey(tmpKey);
        List<String> orgValue = null;
        if(tmpKey.getHashKey() != -1) {
            orgValue = super.get(tmpKey);
            if(orgValue == null) {
                orgValue = new ArrayList<String>();
                super.put(tmpKey, orgValue);
            }
            orgValue.add(tmpValue);
        }
        return orgValue;
    }

    @Override
    public List<String> put(MyKey tmpKey, List<String> tmpValue) {
        populateHashKey(tmpKey);
        List<String> orgValue = null;
        if(tmpKey.getHashKey() != -1) {
            orgValue = super.get(tmpKey);
            if(orgValue == null) {
                orgValue = new ArrayList<String>();
                super.put(tmpKey, orgValue);
            }
            orgValue.addAll(tmpValue);
        }
        return orgValue;
    }

    @Override
    public List<String> get(Object tmpKey) {
        if(!(tmpKey instanceof MyKey)) {
            return null;
        }
        MyKey key = (MyKey) tmpKey;
        populateHashKey(key);
        return super.get(key);
    }
    /**
     * Populates the hashKey generated for the MyKey combination. If the both Key1 and Key 2 are not null and its hash key is not generated
     * earlier then it will generate the hash key using both keys and stores it in class level map 'hashKeyMap' against both keys.
     * @param tmpKey
     */
    public void populateHashKey(MyKey tmpKey) {
        int hashKey = -1;
        if(tmpKey.getMyKey1() != null && this.hashKeyMap.containsKey(tmpKey.getMyKey1()+"_")) {
            hashKey = this.hashKeyMap.get(tmpKey.getMyKey1()+"_");
        } else if(tmpKey.getMyKey2() != null && this.hashKeyMap.containsKey("_"+tmpKey.getMyKey2())) {
            hashKey = this.hashKeyMap.get("_"+tmpKey.getMyKey2());
        }
        /*
         * Assumption - While insertion you will always add first value with Key1 and Key2 both as not null. Hash key will be build only
         * when both keys are not null and its not generated earlier.
         */
        if(hashKey == -1 && tmpKey.getMyKey1() != null && tmpKey.getMyKey2() != null) {
            hashKey = buildHashKey(tmpKey);
            this.hashKeyMap.put(tmpKey.getMyKey1()+"_", hashKey);
            this.hashKeyMap.put("_"+tmpKey.getMyKey2(), hashKey);
        }
        tmpKey.setHashKey(hashKey);
    }

    public int buildHashKey(MyKey tmpKey) {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((tmpKey.getMyKey1() == null) ? 0 : tmpKey.getMyKey1().hashCode());
        result = prime * result + ((tmpKey.getMyKey1() == null) ? 0 : tmpKey.getMyKey1().hashCode());
        return result;
    }
}

测试类 -
import test.map.MultiKeyMap;
import test.map.MyKey;

public class TestMultiKeyMap {

    public static void main(String[] args) {

        System.out.println("=====Add values for each type of key==========");
        MyKey regKey = new MyKey("Key1", "Key2");
        MultiKeyMap myMap = new MultiKeyMap();
        //Register the MyKey having both keys as NOT null.
        System.out.println("Entry 1:"+myMap.addValue(regKey, "Key Reg"));

        MyKey key1 = new MyKey("Key1", null);
        //Add value against MyKey with only Key2
        System.out.println("Entry 2:"+myMap.addValue(key1, "Key1"));

        MyKey key2 = new MyKey(null, "Key2");
        //Add value against MyKey with only Key1
        System.out.println("Entry 3:"+myMap.addValue(key2, "Key2"));

        MyKey bothKey = new MyKey("Key1", "Key2");
        //Add value against MyKey with only Key1
        System.out.println("Entry 4:"+myMap.addValue(bothKey, "both keys"));

        System.out.println("=====Retrieve values for each type of key==========");
        MyKey getKey1 = new MyKey("Key1", null);
        System.out.println("Values for Key1:"+myMap.get(getKey1));

        MyKey getKey2 = new MyKey(null, "Key2");
        System.out.println("Values for Key2:"+myMap.get(getKey2));

        MyKey getBothKey = new MyKey("Key1", "Key2");
        System.out.println("Values for both keys:"+myMap.get(getBothKey));
    }

}

输出 -
=====Add values for each type of key==========
Entry 1:[Key Reg]
Entry 2:[Key Reg, Key1]
Entry 3:[Key Reg, Key1, Key2]
Entry 4:[Key Reg, Key1, Key2, both keys]
=====Retrieve values for each type of key==========
Values for Key1:[Key Reg, Key1, Key2, both keys]
Values for Key2:[Key Reg, Key1, Key2, both keys]
Values for both keys:[Key Reg, Key1, Key2, both keys]

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;
}

1
并不保证所有记录都有key1。 - Ajinkya
如果 key1 不匹配但 key2 匹配呢?散列码将不同,但 equals 将返回 true ... - ppeterka
@ppeterka66:不会发生这种情况,因为我们在此之前进行了数据完整性检查。 - Ajinkya

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现在映射到值D,因为先前由3映射的值与之前步骤中由1映射的值相同。

但是,当您想要将列表(或任何可变对象)放入映射中时,情况就会变得棘手,因为如果您更改列表(添加/删除元素),则该列表将具有另一个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]]

正如您所看到的,仅在将键3转换为映射另一个值后,由12映射的值未被更新。原因是[foo,bar]生成的hashCode[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接口,以便你可以针对其合同提供所有方法的实现,并将你选择的实现作为成员来处理实际映射。


0
请使用Apache Commons Lang库中的优秀辅助类EqualsBuilder和HashCodeBuilder。以下是一个示例:
public class Person {
    private String name;
    private int age;
    // ...

    public int hashCode() {
        return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
            // if deriving: appendSuper(super.hashCode()).
            append(name).
            append(age).
            toHashCode();
    }

    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        if (!(obj instanceof Person))
            return false;

        Person rhs = (Person) obj;
        return new EqualsBuilder().
            // if deriving: appendSuper(super.equals(obj)).
            append(name, rhs.name).
            append(age, rhs.age).
            isEquals();
    }
}

源代码


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

或者(尽管我觉得它更加复杂,也不够灵活),你可以尝试操作键。如果key1.equals(key2)(因此,key2.equals(key1))&& (key1.hashCode() == key2.hashCode()),那么Map.get(key1)将返回与Map.get(key2)相同的值。

听起来不错,但如果其中一个记录有两个键,而另一个记录只有一个键,我该如何生成唯一且相同的键呢? - Ajinkya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接