Can someone explain why the HashMap acts like it does in this example:
Simple test that checks a hashmap for a key. Once in the constructor and once in ListDataListener intervallAdded method.
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) {
}
}
}
Why does the hashmap return false from the event but true from constructor when using containsKey?
Is there some java-generics “magic” I don’t know about here?
Edit:
Just found that ArrayList’s (which ArrayListModel extends) hashCode method assembles its hashcode from all its elements. Which means the hashCode changes with the items in the list.
So storing a ArrayList in a HashMap is not a good idea.
How can I solve this? Store the collections in a holder/container object instead?
I know you understand the problem now, but here is the explanation for other people:
The HashMap uses the hashcode of the key, which in this case is the hashcode of the List.
Looking at the javadoc of the hashcode method of List explains that the hashcode of the list depends on the contained elements in order to respect the contract between hashcode and equality.
Due to the contract any subsequent modification of the list resulting in an equality change will also result in a hashcode change and thus the Hashmap will not be able to retrieve the initial list.
The solution in this case is to use a reference, it will not change when elements are added or removed from the list. But a clone of the list (one that is equal to it) will not work !