Java HashMap无法从ListDataListener事件中找到键

时间:2011-03-13 10:18:10

标签: java arraylist hashmap hashcode

有人可以解释为什么HashMap的行为与此示例中的行为相同:
检查密钥的哈希映射的简单测试。一旦进入构造函数,一次进入ListDataListener intervallAdded方法。

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

import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import com.jgoodies.common.collect.ArrayListModel;

public class Test1 {

  private final Listener listener = new Listener();
  private final Map<List<?>, Object> parentByCollection = new HashMap<List<?>, Object>();

  public Test1(){
    ArrayListModel<Object> list = new ArrayListModel<Object>();

    list.addListDataListener(listener);

    parentByCollection.put(list, new Integer(10));

    // Test containsKey locally
    System.out.println("Item exists (locally):" + parentByCollection.containsKey(list));

    // Test containsKey via ListDataListener
    list.add(new Integer(20));
  }

  /**
   * @param args
   */
  public static void main(String[] args) {
    new Test1();
  }

  public class Listener implements ListDataListener{

    @Override
    public void intervalAdded(ListDataEvent e) {
      List<?> itemSource = (List<?>)e.getSource();

      System.out.println("Item exists (listener):" + parentByCollection.containsKey(itemSource));      
    }

    @Override
    public void intervalRemoved(ListDataEvent e) {
    }

    @Override
    public void contentsChanged(ListDataEvent e) {
    }
  }
}

为什么hashmap从事件返回false,但在使用containsKey时从构造函数返回true? 有一些java-generics“魔法”我不知道在这里?

编辑:

刚刚发现ArrayList(ArrayListModel扩展)hashCode方法从其所有元素组装其哈希码。这意味着hashCode随列表中的项目而变化。 因此,将一个ArrayList存储在HashMap中并不是一个好主意。

我该如何解决这个问题?将集合存储在持有者/容器对象中?

2 个答案:

答案 0 :(得分:2)

我知道你现在理解这个问题,但这是其他人的解释:

HashMap使用密钥的哈希码,在这种情况下是List的哈希码。

查看List的hashcode方法的javadoc解释了列表的哈希码取决于包含的元素,以便尊重哈希码和相等之间的契约。

由于合同,对列表的任何后续修改导致相等更改也将导致哈希码更改,因此Hashmap将无法检索初始列表。

这种情况下的解决方案是使用引用,在列表中添加或删除元素时不会更改。但是列表的克隆(一个等于它的列表)将不起作用!

答案 1 :(得分:0)

当我浏览消息来源时,问题非常明显。在地图中存储集合的解决方案是不使用HashMap,而是使用基于apache commons ReferenceIdentityMap或java.util.IdentityHashMap等引用的地图。