0

I am using overridden hashCode() in one of my objects (instance of A) to generate a unique integer ID for that object. The uniqueness depends only on elements(instances of B) in a ArrayList (I).

Following is the definition of I:

public class I extends ArrayList<B> {
    //object of this class will iterate over this array
    private B[] arrayOfB;

    //other methods and variable to do something
    ...
}

Definition of class B

public class B {
    private SomeEnum type;
    private Object   value; //this can be Integer, String, or some object defined by me

    //getters for both private variables
    ...
}

Definition of class A

public class C implements Iterable {
    private I myIterable;

    @Override
    public int hashCode() {
        int result = 17;
        for(B b: myIterable) {
            result = 31 * result + b.getType().hashCode();
            result = 31 * result + b.getValue().hashCode();
        }
        return result;
    }

    //other methods for this class
    ...
}

As you can see I iterate over all objects in myIterable and construct the final hash. The result depends on type and value contained in each b in myIterable. What I have noticed is that this value changes with different runs of program and this creates a problem as the program is creating duplicate objects.

I can implement the hashCode() for the concerned enumerations but I was wondering if there is some other way to handle this. Following are my questions:

  • Can the hashCode() of equal strings change between runtime?
  • Can the hashCode() of same enum values change between runtime?

NOTE: I am aware that my implementation of A.hashCode() will return different values of hash depending on order of b in myIterable, which is not desired. I am fixing it so that order is not important.

Ashwini Dhekane
  • 2,280
  • 14
  • 19
  • 1
    *"Can the hashCode() of equal strings change between runtime?"* No, the algorithm for `String#hashCode` is [in the JavaDoc](http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#hashCode()) and is reliable for the same sequence of characters. I don't see any such contract for [`Enum#hashCode`](http://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html#hashCode()), but you might check the JLS to see if it provides one. (Commenting as this would be a half-answer.) – T.J. Crowder Jul 15 '13 at 18:32
  • 1
    http://stackoverflow.com/questions/785091/consistency-of-hashcode-on-a-java-string – Dave Newton Jul 15 '13 at 18:32
  • @T.J.Crowder thanks, will check out the enum hashCode implementation. – Ashwini Dhekane Jul 15 '13 at 18:34
  • 1
    @AshwiniDhekane: Not the implementation, the documentation. Implementations can change. :-) Code to the contract, not the implementation. – T.J. Crowder Jul 15 '13 at 18:36
  • Do not depend on any behavior of the implementation that is not documented as part of the API contract. Doing so will almost guarantee that your code will break in the future. – Jim Garrison Jul 15 '13 at 18:36
  • Went through JLS. hashCode() is declared final in enum and it cannot be overriden. I will go with converting enum to string and getting the hashCode of string. – Ashwini Dhekane Jul 15 '13 at 18:43
  • Uniqueness is not a property of values returned from `hashCode()` methods. Even two `String`s can have the same hashCode(). – GriffeyDog Jul 15 '13 at 18:45
  • @GriffeyDog I am implementing hashCode() and equals() with a restriction that if hash codes are equal then the objects are equal. – Ashwini Dhekane Jul 15 '13 at 18:47
  • @AshwiniDhekane - That strikes me as dangerous, as the general contract of `hashCode()` provides no such guarantee. Especially given you are constructing the hashcode from a variable list, which means your hashcode will likely be overflowing on one or more operations, so could conceivably wrap around to a previous seen value. Oh, you'd be better off putting that `hashCode()` definition in `I`, I think, because it seems likely that would be the hashcode of the list anyways (and you shouldn't need to add your own array - the built in one would be fine). And look at `Set`s for ordering... – Clockwork-Muse Jul 15 '13 at 19:58

2 Answers2

2

So do you have different results regardless of the order (for example, when these hashCodes are calculated in isolation)? From hashCode documentation http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode%28%29 :

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.

It looks like theoretically it is possible. However, I think it would be non-standard implementation. Here is the actual source code (openjdk7):

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/String.java#String.hashCode%28%29

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/Enum.java#Enum.hashCode%28%29

plw
  • 59
  • 1
1

There is a difference between what can be done, and what works.

You could subclass (as you have done) and make hashCode() return different values based on the current field settings. That's actually a good thing in one sense of the issue. If the two objects differ by value, then they should return different hashCodes.

However, what you are doing is having one object change its equality to itself. You don't have two objects that become equal, you have one object that decides it isn't equal to "its old self" and possibly won't be equal to its future self.

That's just plain nonsense in the Java world, as no collection is going to monitor its constituents for changes in how they equal themselves (a requirement for some collections, such as maps).

In short, hashCode() provides a means for changing how equality is determined, but not a means for creating a new mathematical property that doesn't follow the definition of equality. You need to maintain A = A for the entire duration of your program execution (and in some cases, even longer).

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • I am implementing equals on the basis of hashCode. Yes, if implementation for String#hashCode() changes in future, my program will go haywire as hashCode of the object becomes primary identifier in my db. I don't know how often that might change. – Ashwini Dhekane Jul 15 '13 at 19:14