Suppose we have domain classes with fields of type
Collection. We also need to implement
equals on these classes, which can be easily done using
org.apache.commons.lang.builder.EqualsBuilder.However, the
EqualsBuilder is not smart enough to treat our data as an unordered collection (unordered is the lowest common denominator when the implemented interface is a
Collection and not a
List). This means that we should wrap our collections into
UnorderedCollectionwhen comparing, which can be done like so:
public static <T, E extends T> Collection<T> asCollection(final Collection<E> ts) {
return new UnorderedCollection<T>() {
@Override
public Iterator<T> iterator() {
return new UnmodifiableIterator<T>() {
private Iterator<E> iterator = ts.iterator();
public boolean hasNext() {
return iterator.hasNext();
}
public T next() {
return iterator.next();
}
};
}
@Override
public int size() {
return ts.size();
}
@Override
public String toString() {
return ts.toString();
}
};
}
private static abstract class UnorderedCollection<T> extends AbstractCollection<T> {
/**
* Taken from {@link AbstractSet#equals} implementation.
* <p>
* <b>Important!</b> not associative when compared with ordered
* {@link Collection} (such as {@link List}).
* </p><p>
* E.g.
* <code>CollectionUtils.asCollection().equals(new ArrayList()) == true</code>
* but
* <code>(new ArrayList()).equals(CollectionUtils.asCollection()) != true</code>
*
* @see java.lang.Object#equals(java.lang.Object)
* @see java.util.collection.AbstractSet#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Collection)) {
return false;
}
Collection c = (Collection) o;
if (c.size() != size()) {
return false;
}
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
}
The implementation of equals was adopted from
AbstractSet as the comments state.
private static abstract class UnmodifiableIterator<T> implements Iterator<T> {
public void remove() {
throw new UnsupportedOperationException(
"Collection was created using CollectionUtils#asCollection which does not support element removal.");
}
}
This way we can wrap any
Collection with an
UnorderedCollection and enjoy the equality between
Arrays.asList(1, 2, 3)
and
Arrays.asList(3, 2, 1)