8

Java provides way to define comparison of object outside scope Object using Comparator.

Now my questions is why java does not allow do same for equals() and hashcode().

Now each collection contains() method can easily use this external equality provider to check objects are equal.

Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
  • Currently the only way to do this is to create a wrapper for keys/elements. Trove4j supports custom HashingStrategies. http://trove4j.sourceforge.net/javadocs/gnu/trove/strategy/HashingStrategy.html – Peter Lawrey Sep 12 '12 at 14:17
  • Possible duplication of [Why not allow an external interface to override hashCode/equals for a HashMap?](http://stackoverflow.com/questions/214136/why-not-allow-an-external-interface-to-override-hashcode-equals-for-a-hashmap) – cubanacan Sep 12 '12 at 14:26
  • @cubanacan That question discusses about specific issue. Here I want to know the thought process behind it. – Amit Deshpande Sep 12 '12 at 14:28
  • Related question [Java: external class for determining equivalence?](http://stackoverflow.com/questions/7259123/java-external-class-for-determining-equivalence/7259151#7259151) – cubanacan Sep 12 '12 at 15:38

3 Answers3

3

Guava has the Equivalence class, which does pretty much what you are asking for.

You can even wrap an Object in an Equivalence to decorate an Object with a better hashCode() equals() implementation (e.g. if you want to use an Object with a bad equals() hashCode() as a Map key but don't have access to the sources)

Here's an example: arrays don't have proper implementations of equals() and hashCode(), but here's an Equivalence for char arrays:

private static final Equivalence<char[]> CHAR_ARRAY_EQUIV = new Equivalence<char[]>(){

    @Override
    protected boolean doEquivalent(char[] a, char[] b) {
        return Arrays.equals(a, b);
    }

    @Override
    protected int doHash(char[] chars) {
        return Arrays.hashCode(chars);
    }
};

Sample code:

final char[] first ={'a','b'};
final char[] second ={'a','b'};

Assert.assertFalse(first.equals(second));
Assert.assertFalse(first.hashCode() == second.hashCode());

final Wrapper<char[]> firstWrapped = CHAR_ARRAY_EQUIV.wrap(first);
final Wrapper<char[]> secondWrapped = CHAR_ARRAY_EQUIV.wrap(second);

Assert.assertTrue(firstWrapped.equals(secondWrapped));
Assert.assertTrue(firstWrapped.hashCode() == secondWrapped.hashCode());
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • That said, the reason that Equivalence isn't as thoroughly supported in Guava -- and probably why it doesn't exist in the JDK -- is that trying to add multiple notions of equality usually just leads to gratuitously confusing code. – Louis Wasserman Sep 12 '12 at 16:14
  • Yeah, that's discussed here: http://stackoverflow.com/a/7259151/342852 I still disagree, but I see your point. – Sean Patrick Floyd Sep 13 '12 at 08:28
  • @LouisWasserman: There *are* different notions of equality; the notion that all equality-test methods should behave the same is IMHO counterproductive. For example, in some contexts it is useful to have positive zero and negative zero compare as equal, but in other contexts one may wish to regard two objects as different unless they could only be distinguished using methods that test reference equivalence. In some contexts, one would want to be able to find "Foo" in a dictionary given "FOO", but clearly such strings should not generally be considered equivalent. – supercat Oct 31 '12 at 22:20
0

Comparator interface is used to compare objects while sorting collections, and its compare() method returns an "int", means, comparision ends with an int value, which can be used to indicate object's place in a sorted collection.

contains() calls equals() method for each instance in a collection in order to find out whether two instances are equal according to equals- contract.

Erhan Bagdemir
  • 5,231
  • 6
  • 34
  • 40
  • `CompareTo` has also contract also `equals returns boolean` how it is different than this. It could have been on the same lines. – Amit Deshpande Sep 12 '12 at 14:31
0

equals and hashCode are concepts that don't change for a given Object. Only the implementor knows what values should be used according to the rules for these methods. Once he decided about those they define the identy of the object and thus should not be changed ever.

Comparasion on the other hand can be highly dependent on the context. You can define a "natural" order by implementing Comparable. But this can't change for different contexts. Say you have a list of Contacts that can be sorted by last name, first name, zip code, city... You can easily do this by providing separate Comparators (or a parametrized Comparator). But it is not inherent to the object itself so it should be a class of its own (it could be implemented as static inner class, depending on your code conventions).

Hauke Ingmar Schmidt
  • 11,559
  • 1
  • 42
  • 50
  • 1
    In my opinion, the concept of equality could definitely change. Lets for example consider Algorithm objects. In some contexts, you might say that two Algorithms are equal if they yield the same output for all possible inputs. However, in a performance measurement context, a brute force Algorithm and an optimized Algorithm would definitely not be equal, even if they yield the same output. – Alderath Sep 12 '12 at 15:08
  • @Alderath That is a vald point. What is `equal` depends on context and business rules, too. It is not the same in every context. That is why it is necessary to implement the methods and why automatic methods (like field-by-field comparasion) can't work all the time. But _once the context is defined_ (which is the hard part) the definition of `equals` should not change. – Hauke Ingmar Schmidt Sep 12 '12 at 15:44
  • Although genuine equivalence is an inherent property of an object, it is often useful to have collections based upon a looser definition of equivalence. For example, if one were compiling a non-case-sensitive language, a Dictionary which uses a non-case-sensitive equivalence relation for the key would be helpful. One could wrap the key in a "non-case-sensitive-string" wrapper object, but that can be awkward. – supercat Nov 18 '12 at 19:30
  • I agree, there are good usecases for this. But in Java `equals` is simply not made for this, it has other semantics. In your usecase I would wrap/modify the collection, not the key, so the collection uses a non-case-sensitive `Comparator` and getter/setter which modify the key. – Hauke Ingmar Schmidt Nov 19 '12 at 14:44