— Disclaimer: I know this is pretty basic stuff but many, many programmers are doing it still wrong —
As a Java programmer you know how to implement equals and that hashCode has to be implemented as well. You use your favorite IDE to generate the necessary code, use common wisdom to help you code it by hand or use annotations. But there is a fourth way: introducing EqualsBuilder (not the apache commons one which has some drawbacks over this one) which implements the general rules for equals and hashCode:
public class EqualsBuilder {
public static interface IComparable {
public Object[] getValuesToCompare();
}
private EqualsBuilder() {
super();
}
public static int getHashCode(IComparable one) {
if (null == one) {
return 0;
}
final int prime = 31;
int result = 1;
for (Object o : one.getValuesToCompare()) {
result = prime * result
+ EqualsBuilder.calculateHashCode(o);
}
return result;
}
private static int calculateHashCode(Object o) {
if (null == o) {
return 0;
}
return o.hashCode();
}
public static boolean isEqual(IComparable one,
Object two) {
if (null == one || null == two) {
return false;
}
if (one.getClass() != two.getClass()) {
return false;
}
return compareTwoArrays(one.getValuesToCompare(),
((IComparable) two).getValuesToCompare());
}
private static boolean compareTwoArrays(Object arrayOne, Object arrayTwo) {
if (Array.getLength(arrayOne) != Array.getLength(arrayTwo)) {
return false;
}
for (int i = 0; i < Array.getLength(arrayOne); i++) {
if (!EqualsBuilder.areEqual(Array.get(arrayOne, i), Array.get(arrayTwo, i))) {
return false;
}
}
return true;
}
private static boolean areEqual(Object objectOne, Object objectTwo) {
if (null == objectOne) {
return null == objectTwo;
}
if (null == objectTwo) {
return false;
}
if (objectOne.getClass().isArray() && objectTwo.getClass().isArray()) {
return compareTwoArrays(objectOne, objectTwo);
}
return objectOne.equals(objectTwo);
}
}
The interface IComparable ensures that equals and hashCode are based on the same instance variables.
To use it your class needs to implement the interface and call the appropiate methods from EqualsBuilder:
public class MyClass implements IComparable {
private int count;
private String name;
public Object[] getValuesToCompare() {
return new Object[] {Integer.valueOf(count), name};
}
@Override
public int hashCode() {
return EqualsBuilder.getHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.isEqual(this, obj);
}
}
Update: If you want to use isEqual directly one test should be added to the start:
if (one == two) {
return true;
}
Thanks to Nyarla for this hint.
Update 2: Thanks to a hint by Alex I fixed a bug in areEqual: when an array (especially a primitive one) is passed than the equals would return a wrong result.
Update 3: The newly added compareTwoArrays method had a bug: it resulted in true if arrayTwo is bigger than arrayOne but starts the same. Thanks to Thierry for pointing that out.


