This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 8 years ago.
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));
}
}
}
`
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;
}
}
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())
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).
Related
This question already has answers here:
HashSet does not seem to realize that two objects are the same.
(3 answers)
Closed 2 years ago.
code
public class Society {
private String address;
private String name;
private Integer noOfFlats;
public Society(String address, String name, int noOfFlats) {
this.address = address;
this.name = name;
this.noOfFlats = noOfFlats;
}
public class SocietySet {
Set<Society> socSet = new HashSet<>();
public void addSocToset(Society society) {
socSet.add(society);
}
public void printSocSet() {
for (Society society : socSet) {
System.out.println("[ "+society.getName()+" ,"+society.getAddress()+"
,"+society.getNoOfFlats()+" ]");
}
}
Main Method
public static void main(String[] args) {
SocietySet societySet = new SocietySet(); // initialized object of class
Society society1 = new Society("pune","kamalapark",15);
Society society2 = new Society("pune","kamalapark",15);
Society society3 = new Society("pune","dsk",50);
societySet.addSocToset(society1);
societySet.addSocToset(society2);
societySet.addSocToset(society3);
societySet.printSocSet();
}
}
its printing same values of first two societies.
output :
[ kamalapark ,pune ,15 ]
[ kamalapark ,pune ,15 ]
[ dsk ,pune ,50 ]
where it should technically print unique values only,
what should be done to stop it from printing common values??
A Set by definition cannot contain two objects with 'equal' values.
Your problem is that your Society class does not have any particular concept of two Society objects being equal.
Society needs to define methods equal() and hashCode().
Add the below 2 methods to your Society class.You must first use the equals() and hashcode() methods to verify that the element is unique.
#Override
public boolean equals(Object obj) {
Society otherObj = (Society) obj;
return this.name.equals(otherObj .name) &&
this.noOfFlats.equals(otherObj .noOfFlats) &&
this.address.equals(otherObj .address) &&
this.hashCode() == otherObj .hashCode();
}
#Override
public int hashCode() {
return (43 + 777);
}
When you put an object into a Hashset it uses the object's hashcode value to determine where to put the object in the Set. But it also compares the object's hashcode to the hashcode of all the other objects in the Hash Set, and if there's no matching hashcode,the HashSet assumes that this new object is not a duplicate. HashSet finds a matching hashcode for two objects. one you're inserting and one already in the set-the HashSet will then call one of the object's equals() methods to see if these hashcode-matched objects really are equal. And if they're equal, the HashSet knows that the object you're attempting to add is a duplicate of something in the Set, so the add doesn't happen.
Override this equals method to check for equality of object along with hashcode.
public boolean equals(Object o)
You will need to override equals and hascode method.
#Override
public boolean equals(Object other) {
if(!other instanceof Society)
return false;
Society o = (Society)other;
return o.address.equals(address) && o.name.equals(name) && o.noOfFlats.equals(noOfFlats)
}
#Override
public int hashCode()
{
// your hascode implementation.
}
For detailed explaination: https://www.geeksforgeeks.org/override-equalsobject-hashcode-method/
I believe the root of your issue is not technical, but conceptual.
Your situation turns around the idea of Sets rejecting duplicates and it is true, Sets work exlusively with unique values. But the thing is that applies only to literals, that is single values individually defined by the user (things like "pune","kamalapark" and 15, when added to a set individually, as independent elements).
However, when, as in your code, a Set is made of objects, not literals, then to make it comply with uniqueness you should use hash and equal methods. In this thread we already have answers and comments covering this thing.
Hope my explanation makes your situation clearer.
Why is the output of this program false? I am expecting true as n object initialize with same string what I am checking for.
public class Test {
public static void main(String[] args) {
Name n = new Name("jyoti", "meher");
Set<Name> s = new HashSet();
s.add(n);
System.out.println(s.contains(new Name("jyoti", "meher")));
}
}
class Name {
String name, title;
public Name(String name, String title) {
this.name = name;
this.title = title;
}
public boolean equals(Object o) {
if (!(o instanceof Name)) {
return false;
}
Name n = (Name) o;
return n.name.equals(name) && n.title.equals(title);
}
}
You have to override hashCode() too, not just equals().
To get the correct output true you need to override the hashCode() method in the Name Class
If two objects have different hashCode values then they will be considered not equal by a HashSet.
Whenever you override equals you should also override hashCode() to ensure it is consistent. This is particularly important when dealing with HashSet and HashMap which rely on the hash code to distribute objects.
You can make your code work with the following (non-optimal) implementation of hashCode:
#Override
public int hashCode() {
return 1;
}
This will force the HashSet to use your equals() method when comparing your Name objects. If you used both name and title when generating the hash code then there would be less occasions when the equals method would have to be used.
In the Name class both equal and hashcode method should override.so that output will be true.
You should override equals() and hashcode() method to ensure the contract is satisfied to use your object as a key in the implementations like HashSet and HashMap.
You have overridden equals correctly. You can override hashcode also like below
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((name == null) ? 0 : name.hashCode());
result = prime * result + id;
result = prime * result
+ ((title == null) ? 0 : title.hashCode());
return result;
}
Overriding hashcode facilitates your object to be stored in different slots based on the hashcode which makes your search faster.
Hope this helps
Here is my code:
import java.util.HashMap;
class MyString
{
String string;
MyString(String string)
{
this.string = new String(string);
}
}
public class test
{
public void test3()
{
HashMap<MyString, Byte> byteHashMap = new HashMap<MyString, Byte>();
char c = 'A';
byte b = (byte)c;
String string = new String("The Letter A");
MyString myString = new MyString(string);
byteHashMap.put(myString, b);
if (byteHashMap.containsKey(myString))
//if (byteHashMap.containsKey(new MyString(string)))
{
System.out.println("true");
}
else
{
System.out.println("false");
}
}
public static void main(String[] args)
{
test testObject = new test();
testObject.test3();
}
}
I would like to know why the code, as is, returns true. However, when I switch the if statements (see the commented line), it returns false.
I believe it has something to do with overriding the equals method in the MyString class but I am unsure how to approach this.
Regards.
Your belief is correct. Map structures rely on .equals to determine if a key already exists in the map or not. By default, object types have instance equality (meaning a.equals(b) is true if and only if a and b point to the same object).
In your case, you probably want two MyString instances to be equal if the string field is equal. In this case, you would override equals with something like:
#Override
public boolean equals(Object other) {
if (other == null) { return false; }
if (!(other instanceof MyString)) { return false; }
MyString m = (MyString)other;
return this.string.equals(m.string);
}
With this, either of the two if statements would return true.
You should also override hashCode. If you use Eclipse, it can generate these two methods for you quite easily via the Source menu.
Yes, override hashCode and equals.
Right now you will compare a string against the memory reference (standard implementation) of your MyString object.
example
#Override
public bool equals(Object args0) {
if(args0 == this) { return true; }
if(!(args0 instanceof MyString)) { return false; }
MyString obj = (MyString) args0;
return obj.getString().equals(this.string); // Create getter
}
#Override
public int hashCode(){
return this.string.hashCode() * 37;
}
You need to implement hashCode and equals. HashMaps put objects in buckets based on the hashCode. Different objects can have the same hashCode, but the rule is that two equal objects must have the same hashCode. Once the HashMap finds all of the entries with the same hashCode, it then checks for equality using the equals method.
The default implementation checks for object instance equality - that is, are the two objects the same instance. In your case, you're checking two different instances.
Override the equals and hashCode methods to pass through the values returned by hashCode() and equals() for the private String member variable, assuming that's all that determines if two instances of your class are equal.
I have Map<Date, String. I have two Date objects a, b that are equal. I put a string value to the map associated with key a. Then I try to get map value associated with keys a and b but only a returns value that I've put. Is it possible to get my value with b key. I know this is possible when keys are simple strings. Why this doesn't work with other type of objects?
public class Main {
public static void main(String[] args) {
Map<Date, String> map = new HashMap<Date, String>();
Date a = new Date(20131105);
Date b = new Date(20131105);
map.put(a, "sweet");
System.out.println(map.get(a));
System.out.println(map.get(b));
}
static class Date {
private int ymd;
public Date(int ymd) {
this.ymd = ymd;
}
public int getYmd() {
return ymd;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Date) {
return ((Date) obj).ymd == ymd;
}
return false;
}
}
}
The output is:
sweet
null
Since you're using an HashMap for storing your date objects, you have to override the hashCode() method because the key objects are stored in the data structure using their hashCode.
Very basic hashCode() overriden (just for illustration):
#Override
public int hashCode(){
return ymd;
}
Output :
sweet
sweet
A hashmap hashes the elements using their hashCode function. For most types the hash code is computed using the reference of the object and this is the case with Date. While in your case the two dates have same value, they are not the same object. Thus they have different references and so their hash codes differ. When you lookup an element in a HashMap its hashCode is used and so as b's hashCode is different from a's hashCode you can not find an element with key a by using b.
You need to implement hashCode() method in your Date class
Add below code in your Date class
#Override
public int hashCode() {
return ymd;
}
output
sweet
sweet
Because String,Integer and all wrapper class override hasCode and equals both the method.
But In your case equals() method will return true but you have not override hasCode() method so it will generate different hashcode for both a and b .So map.get(b) will return null value.
As per the below link
Hashcode and equals
So it is assumed that if 2 objects are equal (that is, equals() returns true), then their hashCodes() must return the same value.
But consider the below example
public class Test {
public static void main(String args[]) {
Demo d1 = new Demo(123);
Demo d2 = new Demo(123);
System.out.println("d1.hashCode()-->" + d1.hashCode());
System.out.println("d2.hashCode()-->" + d2.hashCode());
System.out.println(d1.equals(d2));
}
}
class Demo {
private int name;
Demo(int name) {
this.name = name;
}
#Override
public int hashCode() {
Double d = Math.random() * 1000;
return d.intValue();
}
#Override
public boolean equals(Object o) {
if ((o instanceof Demo) && (((Demo) o).getName() == this.getName())) {
return true;
} else {
return false;
}
}
public int getName() {
return name;
}
}
the output of the above program is
d1.hashCode()-->85
d2.hashCode()-->692
true
Here the question is even though the hash code is different the equals method return true.
So does it mean for equality of object we do not need same hash code
Some implementations (for instance HashMap) depend on the fact that when two instances are equal their hashcode should be the same. However, purely using equals will not check hashcodes for those instances.
In the case of a hashmap, elemets are first divided into different hash buckets based on the hashcodes of objects (for performance reasons). Inside a bucket, equals is used to check object equality. If two equal instances don't have the same hashCode they will end up in different buckets and that might result in unexpected behaviour due to the contract violation.
It is bad implementation of hashCode() , You should follow that contract.
equals has nothing to do with hashcode independently.
But when you use hashing data structure you should must follow this thing.
You should never implement hashCode using random numbers! It should always be computed using the same fields as equals uses. Otherwise, your objects will be unusable as keys in HashMap, HashSet, etc.
Actually the idea is that when you implemente the equals method you should implement the hashCode method, and if a.equals(b) then a.hashCode() == b.hashCode(). Although there isn't anything in the Java compiler or Runtime that enforces this.
In Hashcode it is always required that if two objects are equal then their hashCode always have to be the same other wise the hash collections(HashMap, HashSet etc. wont work correctly)
Well in your case one of the implementations of can be -
public int hashCode() {
return this.getName();
}
OR if you want to mix it up a bit -
public int hashCode() {
return String.valueOf(this.getName()).hashCode();
}
First imp thing you should not use random number to implement the hashcode() method.
#Override
public int hashCode() {
return name.hashcode();
}
#Override
public boolean equals(Object o) {
if(o !=null && o instanceOf Demo)
{
Demo d=(Demo) o;
if(this.d.name==d.name)
{
return true;
}
else
{
false;
}
else
{
return false;
}
}