views:

281

answers:

3

Did you know that :

Map<Object,Object> m1 = new HashMap<Object, Object>();
Map<Object,Object> m2 = new HashMap<Object, Object>();
System.out.println("m1.equals(m2) = "+m1.equals(m2));
System.out.println("m1.keySet().equals(m2.keySet()) = "
            +m1.keySet().equals(m2.keySet()));
System.out.println("m1.entrySet().equals(m2.entrySet()) = "
            +m1.entrySet().equals(m2.entrySet()));
System.out.println("m1.values().equals(m2.values()) = "
            +m1.values().equals(m2.values()));

would output :

m1.equals(m2) = true
m1.keySet().equals(m2.keySet()) = true
m1.entrySet().equals(m2.entrySet()) = true
m1.values().equals(m2.values()) = false

This is caused by the fact that AbstractCollection (which HashMap$Values inherits from) does not overrides #equals().

Do you have an idea why this is so ?

A: 

I am not sure if thatt's the official reason, but AbstractCollection avoids adding semantic constraints on potential subclasses. The semantics of equality are determined by the nature of the concrete inheriting data structure, and in particular based on whether your structure is ordered and whether it allows duplicates.

For example, Consider TreeSet, LinkedList, Bag, etc.

BTW, In relation to the code you posted, what's the actual type of whatever is returned by values? That should be a subclass with a concrete implementation.. If your map is empty when you run this code, it's possible that you would be ending up with something that does not consider two empty sets equal.

Uri
The question mentions the type returned by values() is HashMap$Values
Craig P. Motlin
Right, but what is it? Is it a custom data structure or an existing data structure? I would guess that it is some sort of an iterator?
Uri
+4  A: 

Both AbstractList and AbstractSet extend AbstractCollection, and they have different behaviors for their equals() methods, specified by the interfaces List and Set. The interface for Collection says:

While the Collection interface adds no stipulations to the general contract for the Object.equals, programmers who implement the Collection interface "directly" (in other words, create a class that is a Collection but is not a Set or a List) must exercise care if they choose to override the Object.equals.

So AbstractCollection should definitely not override equals(). That said, I don't really know why HashMap$Values would not implement equals() itself.

Craig P. Motlin
Great answer. Maybe I'll ask another question more specifically about why HashMap$Values does not implement equals() (probably because nobody needs it). I think I will validate Greg Case's answer as yourself seems to prefer its wording.
Michel
+6  A: 

Per the contract of Collection#equals(), there is no general-purpose equals() methods for Collections, and thus AbstractCollection cannot provide one.

Note that HashMap$Values is neither a Set nor a List, thus the quandary and in a sense the reason it does not support equals().

Greg Case
I like your answer, you've said something similar to me but in a better way. Values is not a Set or a List, but you can think of it like a Bag. Why not implement equals() with bag semantics?
Craig P. Motlin
@Motlin : I agree our answers are similar, unfortunately we answered within seconds of each other. As far as bag semantics, a commutative containsAll() check would unfortunately break the contracts of List.equals() and Set.equals() (since they would no longer themselves be commutative).
Greg Case