Reputation: 3444
I have an object that looks like this (I've using a bit of the Guava library in order to simply my equals and hashcode methods):
public class MyClass {
private Collection<Integer> integers;
private Collection<String> strings;
// Getters and Setters...
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MyClass that = (MyClass) o;
return Objects.equal(integers, that.integers) &&
Objects.equal(strings, that.strings);
}
@Override
public int hashCode() {
return Objects.hashCode(integers, strings);
}
}
The issue I'm running into is with code that essentially does this (this would be a typical test case):
// Produce a new instance of MyClass that users ArrayLists.
MyClass expected = new MyClass();
expected.setIntegers(Arrays.asList(1, 2, 3));
expected.setStrings(Arrays.asList("a", "b"));
// I don't normally construct the actual object here,
// but I've included the code so you get an understanding of what's happening
// Produce a new instance of MyClass that uses HashSets.
Set<Integer> someNumbers = new HashSet<Integer>();
someNumbers.add(1);
someNumbers.add(2);
someNumbers.add(3);
Set<String> someStrings = new HashSet<String>();
someStrings.add("a");
someStrings.add("b");
MyClass actual = new MyClass();
actual.setIntegers(someNumbers);
actual.setIntegers(someStrings);
assertEquals(expected, actual);
The problem I'm running into is that even though the contents of the collections are all the same and the compile-time type of those members is "Collection", the run-time types are used to evaluate equality so this assertion fails. Looking at the source code for AbstractSet.equals and AbstractList.equals, they check to see if the other object is a Set or a List, respectively, before they evaluate the contents.
I suppose that makes some sense because order matters in a list and doesn't in a set, so, even if the contents are the same, you can't compare them.
That said, in this case, I don't care what the underlying collection is - I just want to know if the contents are the same and order makes no difference. Is there any easy way to do this?
Upvotes: 0
Views: 133
Reputation: 46422
Just do not do it! There's no possible sane re-definition of equals
for Collection
as its subclasses List
and Set
define it in an incompatible way. So the only possibility is to stick with existing equals
inherited from Object
, i.e., testing reference equality.
Actually, there's a solution for your problem... although it's more of another problem than a solution. If you want to consider integers
equals whenever they're equals as List
(or Set
, you have to choose), you can write a corresponding method by simply copying the loop from AbstractList
(or AbstractSet
).
In case you care about repeated elements, but not about repetitions, you may be able to sort of copy as amit proposed. However, sorting needs Comparable
elements or a Comparator
, so it needn't apply. A very trivial solution is Guava's Multiset.
However, things like the one you're asking for rarely occur in a good design. Usually it's better to
Upvotes: 0
Reputation: 178451
In the case the underlying collection does not matter to you, you could switch to one pre-defined ordered collection that allows repeats, make sure the two new collections are sorted, and then check for equality.
One simple way to do it is convert your elements to arrays using toArray()
, sort it with Arrays.sort()
, and check for equality using Arrays.equals()
Upvotes: 1