Reputation: 41
About HashSet .... The method add() returns true if this set did not already contain the specified element. But if I write:
HashSet<Car> hash = new HashSet<Car>();
Car a = new Car("Ferrari","F40",1987);
Car b = new Car("Ferrari","F40",1987);
System.out.println("Hashcode di a:"+a.hashCode());
System.out.println("Hashcode di b:"+b.hashCode());
System.out.println(hash.add(a));
System.out.println(hash.add(b));
I obtain this:
Hashcode di a: 1824327938
Hashcode di b: 1824327938
true
true
... and I have a set of two elements. I'd expect just one. What's there I don't understand? How I can avoid the second add?
Thank in advance.
Sergio
PS I add my equals method:
public boolean equals(Car c) {
if (brand.equals(c.getBrand()) && model.equals(c.getModel()) && version == c.getVersion())
return true;
else
return false;
}
}
and that's it. It seems that I wrote equals method in a wrong way not considering that equals argoument must be Object and not directly a Car.
Upvotes: 1
Views: 296
Reputation: 48713
After seeing that your method signature was incorrect, I would suggest the following:
If you had placed @Override
on your public boolean equals(Car c)
, you would have had a compile-time error.
@Override
public boolean equals(Car c) { // ERROR
// ... ^ expected Object here
}
This would be caused because the parameter in the method signature was incorrect. The compiler did not know that you mean public boolean equals(Object c)
. When you override a method, always add the annotation. This way you do not experience any undesired behavior.
If you use an IDE like IntelliJ or VSCode (with the "Java Code Generators" extension), you can have them generate constructors, accessors (getters), mutators (setters), toString()
, and even override the equals(Object)
and hashCode()
methods.
If you use Lombok, you can even have it generate overrides via @EqualsAndHashCode
.
User k314159 mentioned pointed out that you can use the record
keyword to define a standard POJO object. For information on this new construct check out: Java 14 Record vs. Lombok. Please keep in mind that this is a feature of JDK 14.
Here is an example of overriding the Object
methods equals
, hashCode
, and toString
:
import java.util.Objects;
public class Car {
private String make;
private String model;
private int year;
public Car() {}
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Car)) return false;
Car car = (Car) other;
return Objects.equals(make, car.make) && Objects.equals(model, car.model) && year == car.year;
}
@Override
public int hashCode() {
return Objects.hash(make, model, year);
}
@Override
public String toString() {
return "{" +
" make='" + getMake() + "'" +
", model='" + getModel() + "'" +
", year='" + getYear() + "'" +
"}";
}
public String getMake() {
return this.make;
}
public void setMake(String make) {
this.make = make;
}
public String getModel() {
return this.model;
}
public void setModel(String model) {
this.model = model;
}
public int getYear() {
return this.year;
}
public void setYear(int year) {
this.year = year;
}
}
import java.util.HashSet;
public class CarDriver {
public static void main(String[] args) {
HashSet<Car> hashSet = new HashSet<Car>();
Car a = new Car("Ferrari", "F40", 1987);
Car b = new Car("Ferrari", "F40", 1987);
System.out.println(hashSet.add(a)); // true
System.out.println(hashSet.add(b)); // false
System.out.println(hashSet);
}
}
Output:
true
false
[{ make='Ferrari', model='F40', year='1987'}]
Here is the latest dependency version for Maven (as of today):
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
The same Car
class above can be compiled using the following annotations:
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Getter
@Setter
@ToString
public class Car {
private String make;
private String model;
private int year;
}
With the @Data
annotation, we can eliminate @ToString
, @EqualsAndHashCode
, @Getter
, and @Setter
. Keep in mind that this annotation will also use the @RequiredArgsConstructor
as well.
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Car {
private String make;
private String model;
private int year;
}
Minimal POM:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<lombok.version>1.18.28</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
The following record
will generate the following methods for you:
public record Car(String make, String model, int year) {}
car.make()
instead of car.getMake()
.Object
method overrides:
equals
, hashCode
, and toString
See also: Java 14 Record Keyword
Upvotes: 4
Reputation: 31
If a.equals(b) is true then there hashcode() must also be same.
Override both .equals() and .hashCode() in your custom class.
Use the same fields of your custom class to calculate hashCode which you used to check equality in .equals().
Yes it'll make sure that there are only unique instances of your customClass in hash-set. So go for it.
Upvotes: 0