-1

I am executing below code after overriding hashcode method of an object (BookMe). Aim is to override hashcode of the object which i will be using as a key in my map (hashmap). But, after executing I see null values. The is no problem with the actual size of map. below is the code. If I don't override hashcode method I get correct output (i mean all three values). `

class BookMe{
private String isbn ;
static int i = 0;
public BookMe(String isbn)
{ 
  this.isbn = isbn;     
}
public String getIsbnValue()
{
    return this.isbn;
}
@Override
public boolean equals(Object o)
{
    if(o instanceof BookMe && ((BookMe)o).getIsbnValue() == this.getIsbnValue())
    {
        return true;
    }
    else{
        return false;
    }
}
@Override
public int hashCode()
{
    return this.isbn.toString().length() + (++i);
}

}

public class HashMapTest {

public static void main(String[] args) {

    Map<BookMe, Integer> map = new HashMap<BookMe, Integer>();

    BookMe b1 = new BookMe("Graham");
    BookMe b2 = new BookMe("Graham");
    BookMe b3 = new BookMe("Graham");
    map.put(b1, 19);
    map.put(b2, 33);
    map.put(b3, 22);

    System.out.println("----444444--------");
    System.out.println(map.size());

    Set <BookMe> set = map.keySet();
    System.out.println("------*****------");


    for(BookMe bk : set)
    {
        System.out.println("bk : "+ bk);
        System.out.println(map.get(bk));
    }       
}

} `

MKod
  • 803
  • 5
  • 20
  • 33
  • You're modifying a static field and using it to affect the returned hashcode. That means the same object won't give the same hashcode on repeated calls. That is completely useless as a hashcode. – khelwood Dec 25 '14 at 11:29
  • This is a likely cause of your problems: `(BookMe)o).getIsbnValue() == this.getIsbnValue()`. See the Q&A I linked to for an explanation. – Stephen C Dec 25 '14 at 11:35
  • @Stephen... I have made changes. I understood error, there is an overlooked code error and i have corrected to "equals". That is not my argument here. please see my third comment from Eran's reply. – MKod Dec 25 '14 at 12:05

3 Answers3

2

Your hashCode violates the contract of Object::hashCode. It should return the same value for the same object.

From the Javadoc :

 * <li>Whenever it is invoked on the same object more than once during 
 *     an execution of a Java application, the <tt>hashCode</tt> method 
 *     must consistently return the same integer, provided no information 
 *     used in <tt>equals</tt> 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. 

If each call to hashCode for the same instance returns a different value, you can't expect your HashMap to locate your keys.

If you want a hashCode based on the ISBN, just return the hashCode of the ISBN :

@Override
public int hashCode()
{
    return this.isbn.hashCode();
}

You should also fix your equals method :

public boolean equals(Object o)
{
    if(o instanceof BookMe && ((BookMe)o).getIsbnValue().equals(this.getIsbnValue()))
    {
        return true;
    }
    else{
        return false;
    }
}
Eran
  • 387,369
  • 54
  • 702
  • 768
  • I will try, but the hashCode for same strings (i.e. in my case "Graham" ) would give same repeated hashCode for my BookMe object. – MKod Dec 25 '14 at 11:39
  • @MKod The hashCode for the same ISBN must give the same value. Otherwise, HashMap won't be able to locate a BookBe key using this hashCode. – Eran Dec 25 '14 at 11:42
  • @Eran...I want to add something...If I dont override I am getting all the three values as explained already. My serious contention here is for every put or get it will call hashcode and then it will figure out the uniqueness of the key. So, If i either overiride or don't, it (hashcode checking) still go into hashcode method to check the uniqueness. Having said that, how come it gives me all three values (I mean when I don't override hashcode method) ? – MKod Dec 25 '14 at 11:55
  • 1
    @MKod When you don't override hashCode and equals, the default implementations from Object class are used. Since the default of equals is simply `==`, your 3 BookMe instances are considered to be unique keys (regardless of whether or not they all have the same hashCode), even though they all have the same ISBN. – Eran Dec 25 '14 at 12:01
  • Thanks Eran. I did some trail and error with == and equals using two String constant's with an without NEW. I am clear on the fundamental. – MKod Dec 25 '14 at 12:18
  • @MKod You should never use == on Strings (or any reference types). – Eran Dec 25 '14 at 12:21
  • @Eran - "or on any reference types" is an over-reach. It depends on the specific semantics of the type (as you have implemented it / and as you use it). For example, it is safe and appropriate to use `==` to compare `Thread` or `Class` instances. And if you *need* an object identity test, then `==` is appropriate there too. – Stephen C Dec 25 '14 at 23:54
0

In addition to what Eran said your hash code is ever going to return new hash code even for the same object say when you are putting the element into map and when you are iterating over the same, You should always use equals method to compare the two string.

((BookMe)o).getIsbnValue() == this.getIsbnValue()

Change it to:

((BookMe)o).getIsbnValue().equals(this.getIsbnValue())
SMA
  • 36,381
  • 8
  • 49
  • 73
0

Your hashcode method is broken and also in equals method you are comparing string by reference not by value . You can correct the equals method like this

@Override
public boolean equals(Object o) {
    return o instanceof BookMe && ((BookMe) o).getIsbnValue().equals(this.getIsbnValue());
}

And to correct hashcode , you can use Objects method to generate hashcode

@Override
    public int hashCode() {
        return Objects.hash(isbn);
    }

The general contract for overridden implementations of this method is that they behave in a way consistent with the same object's equals() method: that a given object must consistently report the same hash value (unless it is changed so that the new version is no longer considered "equal" to the old), and that two objects which equals() says are equal must report the same hash value. There's no requirement that hash values be consistent between different Java implementations, or even between different execution runs of the same program, and while two unequal objects having different hashes is very desirable, this is not mandatory (that is, the hash function implemented need not be a perfect hash).

sol4me
  • 15,233
  • 5
  • 34
  • 34