Hash code implementation in java - java

package MavenWeb.MavenWeb;
import java.util.HashMap;
import java.util.Map;
public class StringEquality {
public static void main(String[] args) {
Person p1 = new Person("Naveen", 22, 1000);
Person p2 = new Person("Naveen", 21, 2000);
Person p3 = new Person("Naveen", 23, 3000);
if(p1.equals(p2)){
System.out.println("P1 and p2 :" + p1.equals(p2));
} else{
System.out.println("P1 and p2 :" + p1.equals(p2));
}
if(p1.equals(p3)){
System.out.println("P1 and p3 :" + p1.equals(p3));
}
Map<Person, Object> map = new HashMap<Person, Object>();
map.put(p1, p1);
map.put(p2, p2);
System.out.println(map.get(new Person("Naveen", 21, 2000)));
}
}
...
class Person{
public Person(String name, int id, float salary){
this.name = name;
this.id = id;
this.salary = salary;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
String name;
Float salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
int id;
#Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(this == obj){
return true;
}
if(obj instanceof Person){
Person person = (Person)obj;
if((person.getName().equals(name)) && person.getId() == id
&& person.getSalary() == salary){
return true;
}
}
return false;
}
#Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + name.hashCode();
hashCode = 31 * hashCode + id;
hashCode = 31 * hashCode + salary.intValue();
return hashCode;
}
#Override
public String toString() {
return "Name :" + name + ", id : " + id;
}
}
I have write the following program. Little bit confused regarding hashcode implementation,
Why we use 31 in implementation
According to equals method logic this program should return p2(Naveen, 21) instance but it is returning null. why?

You're getting null from the map because the salary is stored as a Float (and not float), and you're comparing Float instances with == instead of comparing them with equals().
So the equals() method of Person checks that exactly the same Float instance is in both Person instances, instead of checking that the value of their salary is equal. You should use float (or better: double), unless the salary is nullable (you should also consider using BigDecimal to deal with exact money amounts). But in that case, you'll have to check for null in the Person.equals() and use equals() to compare the salaries. The easiest way, in Java 7 or later, is to use Objects.equals() to compare nullable objects together:
#Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(this == obj){
return true;
}
if(obj instanceof Person){
Person person = (Person)obj;
return Objects.equals(name, person.name)
&& id == person.id
&& Objects.equals(salary, person.salary);
}
return false;
}
#Override
public int hashCode() {
return Objects.hash(name, id, salary);
}

You used the number 31 so it is up to you what makes sense.
The number 31 is used for String as it is an odd prime (which produces more random hash codes) and larger than the number of letters in the alphabet so different letters are less likely to produce the same hash code for different text. With 31 you can have all 5 characters Strings being unique if they use either A-Z or a-z.
However, for longer strings I find that 57 works better for me and for non Strings other, larger prime numbers like 10191 could be better. I like 10191 as 101, 1019 and 10191 are prime numbers.

The problem is that your equals implementation is false.
Consider this :
Person p4 = new Person("Naveen", 21, 2000);
System.out.println(p2.equals(p4));
It prints false, or it should print true.
Or map.get(Object key) need the equals method to check if the key is already store in the map.
More formally, if this map contains a mapping from a key k to a value
v such that (key==null ? k==null : key.equals(k))
What's your problem with your equals method ?
In your equals method, you should replace
person.getSalary() == salary
By
1. person.getSalary().equals(salary)
2. person.getSalary().floatValue() == salary.floatValue()
Because getSalary() returns a Float object, so == will check their references and not their values.
There is no need of a the Float wrapper class, you should use float salary; or double salary; (if you want more precision).
You can use the equals implementations generated by Eclipse :
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary == null) {
if (other.salary != null)
return false;
} else if (!salary.equals(other.salary))
return false;
return true;
}
System.out.println(map.get(new Person("Naveen", 21, 2000)));
prints
Name :Naveen, id : 21

you can read this:
http://www.xyzws.com/javafaq/why-always-override-hashcode-if-overriding-equals/20
Why always override hashcode() if overriding equals()?
In Java, every object has access to the equals() method because it is inherited from the Object class. However, this default implementation just simply compares the memory addresses of the objects. You can override the default implementation of the equals() method defined in java.lang.Object. If you override the equals(), you MUST also override hashCode(). Otherwise a violation of the general contract for Object.hashCode will occur, which can have unexpected repercussions when your class is in conjunction with all hash-based collections.
Here is the contract, copied from the java.lang.Object specialization:
public int hashCode()
Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.
The general contract of hashCode is:
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.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
The default implementation of equals() method checks to see if the two objects have the same identity. Similarly, the default implementation of the hashCode() method returns an integer based on the object's identity and is not based on the values of instance (and class) variables of the object. No matter how many times the values of its instance variables (data fields) change, the hash code calculated by the default hashCode implementation does not change during the life of the object.
Consider the following code, we have overridden equals() method to check if two objects are equal based on the values of their instance variables. Two objects may be stored at different memory addresses but may still be equal base on their instance variable.
public class CustomerID {
private long crmID;
private int nameSpace;
public CustomerID(long crmID, int nameSpace) {
super();
this.crmID = crmID;
this.nameSpace = nameSpace;
}
public boolean equals(Object obj) {
//null instanceof Object will always return false
if (!(obj instanceof CustomerID))
return false;
if (obj == this)
return true;
return this.crmID == ((CustomerID) obj).crmID &&
this.nameSpace == ((CustomerID) obj).nameSpace;
}
public static void main(String[] args) {
Map m = new HashMap();
m.put(new CustomerID(2345891234L,0),"Jeff Smith");
System.out.println(m.get(new CustomerID(2345891234L,0)));
}
}
Compile and run the above code, the output result is
null
What is wrong? The two instances of CustomerID are logically equal according to the class's equals method. Because the hashCode() method is not overridden, these two instances' identities are not in common to the default hashCode implementation. Therefore, the Object.hashCode returns two seemingly random numbers instead of two equal numbers. Such behavior violates "Equal objects must have equal hash codes" rule defined in the hashCode contract.
Let's provide a simple hashCode() method to fix this problem:
public class CustomerID {
private long crmID;
private int nameSpace;
public CustomerID(long crmID, int nameSpace) {
super();
this.crmID = crmID;
this.nameSpace = nameSpace;
}
public boolean equals(Object obj) {
//null instanceof Object will always return false
if (!(obj instanceof CustomerID))
return false;
if (obj == this)
return true;
return this.crmID == ((CustomerID) obj).crmID &&
this.nameSpace == ((CustomerID) obj).nameSpace;
}
public int hashCode() {
int result = 0;
result = (int)(crmID/12) + nameSpace;
return result;
}
public static void main(String[] args) {
Map m = new HashMap();
m.put(new CustomerID(2345891234L,0),"Jeff Smith");
System.out.println(m.get(new CustomerID(2345891234L,0)));
}
}
Compile and run the above code, the output result is
Jeff Smith
The hashcode distribution for instances of a class should be random. This is exactly what is meant by the third provision of the hashCode contract. Write a correct hashCode method is easy, but to write an effective hashCode method is extremely difficult.
For example, From How to Avoid Traps and Correctly Override Methods From java.lang.Object: If you are unsure how to implement hashCode(), just always return 0 in your implementations. So all of your custom objects will return the same hash code. Yes, it turns hashtable of your objects into one (possibly) long linked-list, but you have implemented hashCode() correctly!
public int hashCode(){
return 0;
}
It's legal because it ensures that equal objects have the same hash code, but it also indicates that every object has the same hash code. So every object will be hashed into the same bucket, and hash tables degenerate to linked lists. The performance is getting worse when it needs to process a large number of objects. How to implement a good hash function is a big topic and we will not cover here

Related

Why this two simple objects are not equal?

I have a School class:
public class School {
private String name;
private int id;
private boolean isOpen;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
}
Then I created two instances of School, and compare the equality of the two instances:
public static void main(String[] args) {
//school1
School school1 = new School();
school1.setId(1);
school1.setName("schoolOne");
//school2
School school2 = new School();
school2.setId(1);
school2.setName("schoolOne");
//result is false , why?
System.out.println("school1 == school2 ? " + school1.equals(school2));
}
Even though I set the same id and name to school1 & school2 instances, but school1.equals(school2) returns false, why?
You have to override the equals(Object) method:
Place this in your School class:
#Override
public boolean equals(Object other) {
if (other == this) return true;
if (other == null || !(other instanceof School)) return false;
School school = (School) other;
if (school.id != this.id) return false;
if (!(school.name.equals(this.name))) return false;
if (school.isOpen != this.isOpen) return false;
if (!(school.hashCode().equals(this.hashCode()))) return false;
return true;
}
If you are going to this, it is also wise to override the hashCode() method as well.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) id;
result = prime * result + (name != null ? name.hashCode() : 0);
result = prime * result + (isOpen ? 0 : 1);
return result;
}
Additional Information
I believe this is the best explanation of overriding hashCode().
This answer was posted by dmeister for the following post: SO: Hash Code implementation.
I reference this all the time, and it looks like this functionallity is used in Eclipse when generating the hashCode() method for a given class.
A for nearly all cases reasonable good implementation was proposed in
Josh Bloch's "Effective Java" in item 8. The best thing is to look it
up there because the author explains there why the approach is good.
A short version:
Create a int result and assign a non-zero value.
For every field tested in the equals-Method, calculate a hash code c by:
If the field f is a boolean: calculate (f ? 0 : 1);
If the field f is a byte, char, short or int: calculate (int)f;
If the field f is a long: calculate (int)(f ^ (f >>> 32));
If the field f is a float: calculate Float.floatToIntBits(f);
If the field f is a double: calculate Double.doubleToLongBits(f) and handle the return value like every long value;
If the field f is an object: Use the result of the hashCode() method or 0 if f == null;
If the field f is an array: See every field as separate element and
calculate the hash value in a recursive fashion and combine the values
as described next.
Combine the hash value c with result with:
result = 37 * result + c
Return result
This should result in a proper distribution of hash values for most use situations.
Imagine twins (in real life), even if they have same look and same age and same name, are they equal? No they are not, they are two different "instances".
It is same in Java. Two different instances cannot be (implicitly) equal, because they each exist independently in their part of memory.
However if you want to compare them like that, you can ovveride equals() method or you can create your own new method for comparing.
Even though I set the same id and name to school1 & school2 instances, but school1.equals(school2) returns false, why?
You need to ovveride the equals() method in your School class. Otherwise the default method implementation from Object class.
see default implementation
public boolean More ...equals(Object obj) {
return (this == obj);
}
In your case it is false, since you are creating two objects. making sense ??
For solution Prefer to read.
You have to override the equals() method meaningfully. The default equals() method inherited from Object class check if two reference are referring same object in the memory.
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any non-null
reference values x and y, this method returns true if and only if x
and y refer to the same object (x == y has the value true).
You have created 2 new objects. Now you are comparing 2 object references ... You are not comparing field member values.. So comparison is false.
For primitive datatypes, you wouldn't have this problem.
If you do not override the public boolean equals(Object) method, the version in Object.class will be called:
public boolean equals(Object obj) {
return (this == obj);
}
Just compare the references(If they are exactly the same object)!
So, you have to implement your own equals(Object) in the School.class. Compare these fields:
private String name; // use String.equals(String)
private int id; // use ==
private boolean isOpen; // use ==
By default, .equals() does "==" ie., comapring references. You have to override equals().
The simple answer is, equals of the implicit super class Object is being used for comparison.
From the documentation:
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Equals method
You would have to override the equals and hashcode method in your Student class.
If you don't override the equals(), then the default equals() in Java.lang.Object will be called:
public boolean equals(Object obj) {
return (this == obj);
}
As you see, it compares the references of two object, so in your case it returns false.
If you want to compare the contents of two object, you can:
#Override
public boolean equals(Object obj) {
// A simple impl. Pls add some checking for null/class type/.. yourself
return this.name.equals(obj.getName()) && this.id == obj.getId() && this.isOpen == isOpen();
}

HashSet doesn't contain my custom object even though I override equals(Object o)

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

HashSet contains duplicate entries

A HashSet only stores values ones, when the equals method says that they're the same. Thats what I thought.
But now i'm adding Elements to a HashSet where the equals method returns true and the size of the set still grows?? sorry I'm confused. Some hints where i'm wrong would be nice.
Element t1 = new Element(false, false, false, false);
Element t2 = new Element(true, true, true, true);
Element t3 = new Element(false, false, false, false);
if (t1.equals(t3))
System.out.println("they're equal");
Set<Element> set = new HashSet<>();
set.add(t1);
set.add(t2);
set.add(t3);
System.out.println("set size: " + set.size());
so in this example my console output is:
they're equal
set size: 3
That makes no sense to me.. shouldn the size be 2?
The problem is that your Element class has not overridden the equals and hashCode methods or these implementations are broken.
From Object#equals method javadoc:
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of -x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
From Object#hashCode method javadoc:
The general contract of hashCode is:
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.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
Make sure the implementations of these methods satisfy these rules and your Set (backed by a HashSet) will work as expected.
Your objects have different hashes so HashSet "puts" then in different "buckets".
If you have your own model classes you need to change some basic functions work like done in the below example.
Execution code :
HashSet<MyModel> models = new HashSet<MyModel>();
for (int i = 1; i < 5; i++)
models.add(new MyModel(i + "", "Name :" + i + ""));
for (int i = 3; i < 5; i++)
models.add(new MyModel(i + "", "Name :" + i + ""));
for (Object object : models)
System.out.println(object);
Model Class :
/**
* Created by Arun
*/
public static class MyModel {
private String id = "";
private String name = "";
public MyModel(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return getId();
}
#Override
public boolean equals(Object obj) {
return !super.equals(obj);
}
public int hashCode() {
return getId().hashCode();
}
}
Hope this helps.
Yes We Can implement it with the object of the classes which are not FINAL.
HashSet Checks for two methods hashCode() and equals() before adding any Object.
First it checks for the method hashCode(),if it returns the hashcode which is same with any of the object in Set, then it checks for the equals method for that object,which internally compares the references for both objects i.e this.obj1==obj.If these are the same references in that case it returns true means it is a duplicate value.
We can add duplicate non-final objects by overriding HashCode and equals method.
In HashCode() you can return same hashcode in case of same parameters.
See example:
public class Product {
int i;
Product(int a)
{
this.i=a;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
#Override
public boolean equals(Object obj) {
/*if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
if (i != other.i)
return false;
return true;*/
return true;
}
}
`
`
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Product p1=new Product(1);
Product p2=new Product(1);
Product p3=new Product(1);
Set s=new HashSet();
s.add(p1);
s.add(p2);
s.add(p3);
System.out.println(s.size());
}
}
The output will be 1.
P.S:Without overriding these methods,output will be 3 since it will use their default behavior.

how to override this hashCode method?

as for the definition of Person:
public class Person {
private int id;
private int characteristics;
public boolean equals (Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Person) {
if (id == ((Person) obj).id) {
return true;
} else if (characteristics == ((Person) obj).characteristics) {
return true;
}
}
return false;
}
}
cause 2 Person objects a and b must have the identical hash code if a.equals(b) returns true, how should I implement the hashCode method?
solution
my equals method implementation is incorrect according to Java's equivalence protocol: transitivity is not satisfied: a.id = 1, a.characteristic = 2, b.id = 1, b.characteristic = 3, c.id = 2, c.characteristic = 3; a.equals(b) == true, b.equals(c) == true, but a.equals(c) == false.
Since your class considers objects equal when either of their respective id or characteristics fields are equal, the only hash code you can reasonably use here is a constant value for all instances:
public int hashCode() {
return 0;
}
This will make hash-based lookups perform horribly.
An either-or test in equals() is generally a bad idea; the objects aren't actually equal, are they? Maybe they are just a "match for each other?" Perhaps you should consider leaving equals() alone and implementing some other comparison method.
As Thomasz pointed out, your equals() test is not transitive; if a.equals(b) && b.equals(c) is true then a.equals(c) must be true. This is not true with your overload, and therefore your implementation breaks the contract of equals(). I would strongly urge you to implement this test in a different method and leave equals() alone.
This is what intellij-idea auto-generated for your class:
#Override
public int hashCode() {
int result = id;
result = 31 * result + characteristics;
return result;
}
And after few refactorings:
#Override
public int hashCode() {
return 31 * id + characteristics;
}
And for the record, is it just me or is your equals() broken? You consider two objects equal if either ids or characteristics are equal, but not necessarily both of them. This means your equality is not transitive which might have really unexpected side effects once your object goes into wilderness.
Here is a decent implementation:
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Person)) {
return false;
}
Person person = (Person) o;
return characteristics == person.characteristics && id == person.id;
}
If having the same id implies always having the same characteristics (which seems necessary for your equals() to be valid), then your hash code can use characteristics alone:
#Override
public int hashCode() {
return characteristics;
}
If that's not the case, you may want to reconsider using Java equality to express this relation, as #cdhowie suggests.

How to override equals method in Java

I am trying to override equals method in Java. I have a class People which basically has 2 data fields name and age. Now I want to override equals method so that I can check between 2 People objects.
My code is as follows
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
But when I write age.equals(other.age) it gives me error as equals method can only compare String and age is Integer.
Solution
I used == operator as suggested and my problem is solved.
//Written by K#stackoverflow
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K#stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
run:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
Introducing a new method signature that changes the parameter types is called overloading:
public boolean equals(People other){
Here People is different than Object.
When a method signature remains the identical to that of its superclass, it is called overriding and the #Override annotation helps distinguish the two at compile-time:
#Override
public boolean equals(Object other){
Without seeing the actual declaration of age, it is difficult to say why the error appears.
I'm not sure of the details as you haven't posted the whole code, but:
remember to override hashCode() as well
the equals method should have Object, not People as its argument type. At the moment you are overloading, not overriding, the equals method, which probably isn't what you want, especially given that you check its type later.
you can use instanceof to check it is a People object e.g. if (!(other instanceof People)) { result = false;}
equals is used for all objects, but not primitives. I think you mean age is an int (primitive), in which case just use ==. Note that an Integer (with a capital 'I') is an Object which should be compared with equals.
See What issues should be considered when overriding equals and hashCode in Java? for more details.
Item 10: Obey the general contract when overriding equals
According to Effective Java, Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply:
Each instance of the class is inherently unique. This is true for classes such as Thread that represent active entities rather than values. The equals implementation provided by Object has exactly the right behavior for these classes.
There is no need for the class to provide a “logical equality” test. For example, java.util.regex.Pattern could have overridden equals to check whether two Pattern instances represented exactly the same regular expression, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is ideal.
A superclass has already overridden equals, and the superclass behavior is appropriate for this class. For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap.
The class is private or package-private, and you are certain that its equals method will never be invoked. If you are extremely risk-averse, you can override the equals method to ensure that it isn’t invoked accidentally:
The equals method implements an equivalence relation. It has these properties:
Reflexive: For any non-null reference value x, x.equals(x) must return true.
Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.
For any non-null reference value x, x.equals(null) must return false.
Here’s a recipe for a high-quality equals method:
Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization but one that is worth doing if the comparison is potentially expensive.
Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.
Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return true; otherwise, return false. If the type in Step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility.
For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, call the equals method recursively; for float fields, use the static Float.compare(float, float) method; and for double fields, use Double.compare(double, double). The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double values; While you could compare float and double fields with the static methods Float.equals and Double.equals, this would entail autoboxing on every comparison, which would have poor performance. For array fields, apply these guidelines to each element. If every element in an array field is significant, use one of the Arrays.equals methods.
Some object reference fields may legitimately contain null. To avoid the possibility of a NullPointerException, check such fields for equality using the static method Objects.equals(Object, Object).
// Class with a typical equals method
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
#Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}
#Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
When comparing objects in Java, you make a semantic check, comparing the type and identifying state of the objects to:
itself (same instance)
itself (clone, or reconstructed copy)
other objects of different types
other objects of the same type
null
Rules:
Symmetry: a.equals(b) == b.equals(a)
equals() always yields true or false, but never a NullpointerException, ClassCastException or any other throwable
Comparison:
Type check: both instances need to be of the same type, meaning you have to compare the actual classes for equality. This is often not correctly implemented, when developers use instanceof for type comparison (which only works as long as there are no subclasses, and violates the symmetry rule when A extends B -> a instanceof b != b instanceof a).
Semantic check of identifying state: Make sure you understand by which state the instances are identified. Persons may be identified by their social security number, but not by hair color (can be dyed), name (can be changed) or age (changes all the time). Only with value objects should you compare the full state (all non-transient fields), otherwise check only what identifies the instance.
For your Person class:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Reusable, generic utility class:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* #param instance object instance (where the equals() is implemented)
* #param other other instance to compare to
* #param stateAccessors stateAccessors for state to compare, optional
* #param <T> instance type
* #return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
For your Person class, using this utility class:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Since I'm guessing age is of type int:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
if age is int you should use == if it is Integer object then you can use equals().
You also need to implement hashcode method if you override equals. Details of the contract is available in the javadoc of Object and also at various pages in web.
tl;dr
record Person ( String name , int age ) {}
if(
new Person( "Carol" , 27 ) // Compiler auto-generates implicitly the constructor.
.equals( // Compiler auto-generates implicitly the `equals` method.
new Person( "Carol" , 42 )
)
) // Returns `false`, as the name matches but the age differs.
{ … }
Details
While your specific problem is solved (using == for equality test between int primitive values), there is an alternative that eliminates the need to write that code.
record
Java 16 brings the record feature.
A record is a brief way to write a class whose main purpose is to transparently and immutably carry data. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
equals method provided automatically
The default implicit equals method compares each and every member field that you declared for the record. The members can be objects or primitives, both types are automatically compared in the default equals method.
For example, if you have a Person record carrying two fields, name & age, both of those fields are automatically compared to determine equality between a pair of Person objects.
public record Person ( String name , int age ) {}
Try it.
Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;
boolean samePerson1 = alice.equals( alice2 ) ; // true.
boolean samePerson2 = alice.equals( bob ) ; // false.
You can override the equals method on a record, if you want a behavior other than the default. But if you do override equals, be sure to override hashCode for consistent logic, as you would for a conventional Java class. And, think twice: Whenever adding methods to a record, reconsider if a record structure is really appropriate to that problem domain.
Tip: A record can be defined within another class, and even locally within a method.
Here is the solution that I recently used:
public class Test {
public String a;
public long b;
public Date c;
public String d;
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Test)) {
return false;
}
Test testOther = (Test) obj;
return (a != null ? a.equals(testOther.a) : testOther.a == null)
&& (b == testOther.b)
&& (c != null ? c.equals(testOther.c) : testOther.c == null)
&& (d != null ? d.equals(testOther.d) : testOther.d == null);
}
}
For lazy programmers: lombok library is very easy and time saving. please have a look at this link
instead of writing lines of codes and rules, you just need to apply this library in your IDE and then just #Data and it is Done.
import lombok.Data;
#Data // this is the magic word :D
public class pojo {
int price;
String currency;
String productName;
}
in fact in the above code, #Data is a shortcut for
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter
#Setter
#EqualsAndHashCode
#ToString
//or instead of all above #Data
public class pojo {
int price;
String currency;
String productName;
}

Categories

Resources