Whether I need to define my own hash and equal method? - java

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.

Related

Add unique elements in HashSet based on attributes of framework provided non-editable object classes

I am trying to generate a HashSet containing unique Employee instances. Uniqueness should be established based on the object properties.
The problem is that I end up with having duplicates.
Note that Employee class is provided by a framework, and it's not possible to provide custom implementations for equals() and hashCode().
Employee class:
public class Employee {
private long employeeId;
private String name;
// getters, setters
#Override
public String toString() {
return "Employee{" +
"employeeId=" + employeeId +
", name='" + name + '\'' +
'}';
}
}
Map<String, Set<Employee>> ackErrorMap = new HashMap<>();
Employee emp = new Employee(1,"bon");
Employee emp2 = new Employee(1,"bon");
ackErrorMap.computeIfAbsent("key1",
x -> new HashSet<>()).add(emp);
ackErrorMap.computeIfAbsent("key1",
x -> new HashSet<>()).add(emp2);
This would result in a Set mapped to the Key "key1" containing both emp and emp2, although object attributes are same.
Is there any way to discard Employee with the same name? Or would it better to use ArrayList?
Possible example using Arraylist
ackErrorMap.computeIfAbsent("key1",
x -> new ArrayList<>()).add(emp);
You can override the equals and hashCode methods in the Employee class. The equals method should return true if two objects are considered equal, and the hashCode method should return the same value for two objects that are considered equal.
class Employee {
private int id;
private String name;
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id &&
Objects.equals(name, employee.name);
}
#Override
public int hashCode() {
return Objects.hash(id, name);
}
}
With these changes, when you add emp and emp2 to the HashSet, only one of them will be added, because they will be considered equal based on the equals method.
You can create a custom type wrapping the class coming from the framework and implement the equals/hashCode contract according to your requirements.
That's how such wrapper might look like (for the purpose of conciseness I'm using a Java 16 record, but it can be implemented as a class as well).
public record EmployeeWrapper(Employee employee) {
#Override
public boolean equals(Object o) {
return o instanceof EmployeeWrapper other
&& employee.getName().equals(other.employee.getName());
}
#Override
public int hashCode() {
return Objects.hash(employee.getName());
}
}
And you can use with a Map of type Map<String,Set<EmployeeWrapper>> to ensure uniqueness of the Employee based on the name property.
I would also advise to make one step further and encapsulate the Map into a class which would expose the methods covering all scenarios of interaction with the Map (like add new entry, get employees by key, etc.), so that your client would not dial wrappers, but only with employees and wrapping and unwrapping would happen within the enclosing class.
Here's how it might be implemented:
public class AcrErrors {
private Map<String, Set<EmployeeWrapper>> ackErrorMap = new HashMap<>();
public void addEmployee(String key, Employee employee) {
EmployeeWrapper wrapper = new EmployeeWrapper(employee);
ackErrorMap
.computeIfAbsent(key, x -> new HashSet<>())
.add(wrapper);
}
public List<Employee> getEmployees(String key) {
return ackErrorMap.getOrDefault(key, Collections.emptySet()).stream()
.map(EmployeeWrapper::employee)
.toList();
}
// other methods
}
You need to override equals() and hashCode() inside Employee class.
Or you can use lombok’s #EqualsAndHashCode annotation in your Employee class.

Comparing objects exercise (Java)

hopefully this doesn't make me seem to be an idiot but I seem to be failing on a simple exercise where I have to compare two objects to check if they are equal, my Java class is below along with the error message I'm getting from the exercise. Would anyone know how to solve it? Thanks in advance.
import java.util.Objects;
public class Person {
private String name;
private SimpleDate birthday;
private int height;
private int weight;
public Person(String name, SimpleDate birthday, int height, int weight) {
this.name = name;
this.birthday = birthday;
this.height = height;
this.weight = weight;
hashCode();
}
public String getName(){
return this.name;
}
public SimpleDate getBirthday(){
return this.birthday;
}
public Integer getHeight(){
return this.height;
}
public Integer getWeight(){
return this.weight;
}
// implement an equals method here for checking the equality of objects
#Override
public boolean equals(Object compared){
return this==compared;
}
}
Error message
Joshua Bloch in Effective Java gives guidelines on how to write a nice .equals(). Here's the excerpt directly from the book:
Use the == operator to check if the argument is a reference to this object.
Use the instanceof operator to check if the argument has the correct type.
Cast the argument to the correct type.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object.
When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(!(o instance of Person)) {
return false;
}
//you comparing logic here
}
You have to make sure that equals follows its contract (it's an equivalence relation). See it's documentation for more details. Also, override the hashcode() method.
You equals method is written wrongly as it just compares the location of objects in memory. That's why your tests are failing.
You changed behaviour of equals to == here:
#Override
public boolean equals(Object compared){
return this==compared;
}
and now here is already an answer - https://stackoverflow.com/a/13387787/7505731

Why this Java Code is compiling successfully

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();
}

why i cant remove the object with override

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.

Confusion on .equals method

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!

Categories