I haven't overridden much of hashCode() and equals() methods so I may be wrong
My question is for the last line where
dep1.equals(emp2) is being compiled successfully(why) (I am expecting compilation error as they have different types) and after compiling I get following
15 15 false
where I am expecting 15 15 true since I am checking the hashcode in the equals method.
class Employee {
private String name;
private int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public int hashCode() {
return this.id;
}
public boolean equals(Employee employee) {
return this.hashCode() == employee.hashCode();
}
public int getEmployeeId() {
return this.id;
}
}
class Department {
private String name;
private int id;
public Department(String name, int id) {
this.name = name;
this.id = id;
}
public int hashCode() {
return this.id;
}
public boolean equals(Department department) {
return this.hashCode() == department.hashCode();
}
public int getDepartmentId() {
return this.id;
}
}
public class JavaCollections {
public static void main(String args[]) {
Employee emp2 = new Employee("Second Employee", 15);
Department dep1 = new Department("Department One", 15);
System.out.println(dep1.hashCode()+" "+emp2.hashCode()+" " + dep1.equals(emp2));
}
}
First, for the reason why this compiles: all classes in Java inherit from java.lang.Object, which defines equals(Object) method, and provides a default implementation. This is the method that you call when you compare an Employee and a Department, not one of the overloads that you have provided.
Your equals code compiles fine, because the compiler does not know that you thought you were overriding equals when you actually didn't. The compiler thinks that you want to make a new method
public boolean equals(Department department)
to compare Department objects to other Department objects.
If you are writing a code that overrides a method of a superclass, add #Override annotation to it, like this:
#Override
public boolean equals(Department department)
Now the compiler will correctly complain to you that your method does not in fact override a method in its base class, alerting you to the problem at compile time.
To fix your code change the signatures of equals to take Object, add #Override, check for null and for the correct type, do the cast, and then do the actual comparison:
#Override
public boolean equals(Department obj) {
if (obj == null || !(obj instanceof Department)) {
return false;
}
Department dept = (Department)obj
return dept.id == id;
}
Note: Implementing equals like this
return this.hashCode() == department.hashCode();
is very fragile. Although it works in your case, when hash code is a unique ID of the object, this wouldn't survive a code refactoring when hashCode is replaced with some other implementation, for example, an implementation that considers both id and name. If you want to rely on comparing IDs, compare IDs directly, without calling hashCode to get them.
That's because both of classes Employee and Department still have not overriden methods public boolean equals(Object obj) inherited from Object class.
Exactly this method is invoked in dep1.equals(emp2), not public boolean equals(Department department).
More specifically, read JLS:
An instance method mC declared in or inherited by class C, overrides from C another method mA declared in class A, iff all of the following are true:
...
The signature of mC is a subsignature (ยง8.4.2) of the signature of mA.
In this case boolean equals(Department department) is not subsignature of boolean equals(Object obj).
First, this code dep1.equals(emp2) calls default implementation of Object class.
Second, U didnt overrides the default implementation in both of your class becoz u cant override equal method for specific customizied types.
If u need ur answer to be 15 15 true
replace
public boolean equals(Department department) {
return this.hashCode() == department.hashCode();
}
by
#override
public boolean equals(Object department) {
return this.hashCode() == department.hashCode();
}
Related
This question already has answers here:
Why do I need to override the equals and hashCode methods in Java?
(31 answers)
Closed 4 years ago.
this is my Student Class, I want to differentiate Student Object on the basis of Name field. But When main class executed i am getting wrong output.
public class Student {
int id;
String name;
public Student(String name, int id)
{
this.name=name;
this.id= id;
}
public int hashCode()
{
return this.id;
}
public String toString()
{
return "Student: "+this.name+"#"+Integer.toHexString(hashCode());
}
public boolean equals(Object o)
{
if(o instanceof Student)
{
String name=this.name;
Student s=(Student)o;
if(s.name.equals(name))
return true;
else
return false;
}
else
return false;
}}
//this is my main class
public class HashSett {
public static void main (String[] args)
{
HashSet<Student> h=new HashSet<>();
h.add(new Student("Nimit",1));
h.add(new Student("Rahul",3));
h.add(new Student("Nimit",2));
System.out.println(h);
}
}
//this is wrong output i got
[Student: Nimit#1, Student: Nimit#2, Student: Rahul#3]
why two times "Nimit" objects are added???
Your hashCode doesn't match your equals implementation. If a.equals(b) is true, a.hashCode == b.hashCode() must also be true.
Since equals only requires that the names are equal, hashCode should return name.hashCode().
public int hashCode()
{
return name.hashCode();
}
Generate toString, hashCode and equals from Eclipse generate option or similar option from any IDE
In the collection test, I create a class named Name and override the equals method, like following,
class Name implements Comparable<Name>{
private String firstName, lastName;
Name(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public String toString(){
return firstName + " "+lastName;
}
public boolean equals(Name name){
return firstName.equals(name.firstName) && lastName.equals(name.lastName);
}
public int hashCode(){
return firstName.hashCode();
}
But When I test the remove() function in collection, it was false and the Name("Andy","Light") is still in the collection. What is the wrong with my code?
public static void main(String[] args){
Collection c = new HashSet();
c.add("hello");
c.add(new Name("Andy","Light"));
c.add(new Integer(100));
c.remove("hello");
c.remove(new Integer(100));
System.out.println(c.remove(new Name("Andy","Light")));
System.out.println(c);
}
There is a comment and an answer that says your hashCode() method is not consistent with equals() because you didn't include lastName in the hash code calculation. They are both wrong.
The hashCode() implementation is allowed to use a subset of the values used by equals(). It will cause more hashcode collisions that way, offsetting improved speed of hashCode() vs degraded performance of hash-buckets. A subset hashcode may be ok, it depends on likelihood of Name objects having same firstName.
Your problem is that the signature of equals() is wrong. It has to be boolean equals(Object).
boolean equals(Name) is not an override of boolean equals(Object), so you didn't actually override/implement the equals() method, and as such ended up with hashCode() being inconsistent with equals() (but not for the reason the others said).
If you add the #Override annotation, the compiler would have caught this problem. Always use the annotation.
Change to:
#Override
public boolean equals(Object obj) {
if (! (obj instanceOf Name))
return false;
Name that = (Name)obj;
return this.firstName.equals(that.firstName) && this.lastName.equals(that.lastName);
}
#Override
public int hashCode() {
return this.firstName.hashCode();
}
This of course assumes that neither can be null.
As #MickMnemonic says in a comment:
It's considered bad practice to leave out fields that are included in equals()
To include lastName in the calculation, use Objects.hash():
#Override
public int hashCode() {
return Objects.hash(this.firstName, this.lastName);
}
Also, as #StephenB said in a comment:
You also need to add a compareTo method because you are implementing Comparable<Name>.
Here you use Name as a parameter, not Object, because of the generic type argument to Comparable.
Example (if sorting by first name before last name):
#Override
public int compareTo(Name that) {
int cmp = this.firstName.compareTo(that.firstName);
if (cmp == 0)
cmp = this.lastName.compareTo(that.lastName);
return cmp;
}
That implements a firstName/lastName lexicographical ordering. You may want to use compareToIgnoreCase() or maybe a Collator for correct localized ordering.
If I need to use InfoName as the key of a HashMap, do I need to define my own hashCode() and equals() method? I think it's not necessary, since the String name variable will be enough to make sure each object of InfoName is different.
public class InfoName {
enum Type {
company, product;
}
public String name;
public Type type;
public InfoName(String name, Type type) {
this.name = name;
this.type = type;
}
}
the String "name" variable will be enough to make sure each object of
InfoName is different
If you only want to use the name in the InfoName, then just make String type name as the key as it already override equals() and hashCode() .
OR
You need to override equals() and hashCode() in InfoName class, else how would JVM knows on which attribute/criteria you are using for hashing and equality check.
If you are sure to have InfoName as key you need to override both.
You an have something like
public class Test {
enum Type {
company, product;
}
public String name;
public Type type;
public Test(String name, Type type) {
this.name = name;
this.type = type;
}
#Override
public boolean equals(Object o) {//or do what you like
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
if (!name.equals(test.name)) return false;
if (type != test.type) return false;
return true;
}
#Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + type.hashCode();
return result;
}
}
Basically your editor provides features of overriding hashcode and equals. Have a look here Why do I need to override the equals and hashCode methods in Java?enter link description here
I think it's not necessary, since the String "name" variable will be
enough to make sure each object of InfoName is different.
I would recommend not to use name as the hash key because it seems a bad candidate. I mean could you have multiple objects with the same product name? In that case you would have many collisions.
Suppose I have a class Employee:
class Employee{
int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
class Example {
public static void main(String[] args){
Employee e1=new Employee();
Employee e2=new Employee();
e1.setId(1);
e2.setId(1);
System.out.println(e2.equals(e1));
}
}
Why does it gives false?
What is the reason for it, need a brief explanation for .equals and == method.
All classes inherit from Object
Therefore they use the Object.equals method until you override it
Object.equals tests for reference equality, it knows nothing about the fields in your class and cannot test for "value" equality
i.e. to test for value equality you need to override equals and provide your own implementation. As an example:
class Employee{
int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
//see comments below for this next line
if (o.getClass() != this.getClass()) return false;
Employee other = (Employee)o;
return other.id == this.id;
}
}
Your override should satisfy the rules of reflexivity, symmetry, transitivity, consistency, and be false for a null argument, hence the complexity in the above example. To do this it does:
a reference check (for efficiency)
a null check
either an instanceof, or a getClass check (the choice between these two depends on your definition of equality for subtypes)
a cast to the same type
finally, the value field checks
Note also that overriding equals means you should also override hashCode:
#Override public int hashCode()
{
return id;
}
== will compare if two objects are the same reference, so from your example:
e1 == e1; // true
e1 == e2; // false
equals checks if two objects are logically equal. The default implementation will do just as == does, therefore you need to provide your own implementation. For your example a very trivial implementation would be:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee other = (Employee) o;
return id == other.id;
}
About .equals() and == you can read this thread to find more :-)
About your question, and as said above, you need to override .equals() method in class Example (both of them) the reason for that is because in Java as you may already know, almost everything is subclass of Object therefore it uses the methods of Object and thats the reason why .equals() method apears even if its not implemented in your code.
To override the .equals() either do it manually or if you use eclipse, go to Source>Generate hashCode() and equals()... and when the 2 methods are overwritten in your code, you can edit them in a way they suit your code :)
Hope i helped!
After adding two identical objects to a Set, I would expect the set to contain only one element.
public void addIdenticalObjectsToSet(){
Set<Foo> set = new HashSet<Foo>();
set.add(new Foo("totoro"));
set.add(new Foo("totoro"));
Assert.assertEquals(1, set.size()); // PROBLEM: SIZE=2
}
private class Foo {
private String id;
public Foo(String id) {
this.id = id;
}
public String getId() {
return id;
}
public boolean equals(Object obj) {
return obj!= null && obj instanceof Foo &&
((Foo)obj).getId().equals(this.getId());
}
public int hashcode() {
return this.getId().hashCode();
}
}
I consider two objects as identical if they have the same id (String).
Other strange thing: Neither Foo.equals nor Foo.hashcode are accessed, as far as I can tell using debug/breakpoints. What am I missing?
public int hashcode() {
return this.getId().hashCode();
}
should be
#Override
public int hashCode() {
return this.getId().hashCode();
}
The annotation would have told you about the spelling mistake.
There should also be a (missing) little triangle symbol in your IDE on the method to indicate if an interface is being implemented or a parent method overridden.