Switching a compare to function to a boolean in Java - java

I am trying to change the compareTo after #override to a boolean function. This function will take a generic Object o and return whether or not it equals that Ingredient object. The only attribute in ingredient is name. So cast Object o to a Ingredient and then return whether the string names equal.
public class Ingredient implements Comparable<Ingredient> {
private String name;
public Ingredient(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
/**
* Compares two ingredients.
*
* note: if we add other ingredient attributes,
* we will need to change this method.
*/
#Override
public int compareTo(Ingredient other) {
return this.name.compareTo(other.name);
}
}
So, I change the last function to:
Does this work? Or, am I on the right track...
public boolean equals(Object o){
if(this == o) return true;
Ingredient ingredient = (Ingredient)o;
if(ingredient.name.equals(this.name)) {
return true;
} else return false;
}

You don't even have to use an interface.
According to the Java Docs, a Comparable is being used specify and order, e.g. sorting an Array.
To check for equality, you have to override the method equals (and not even implement some interface), which returns a boolean:
true for equality
false for inequality
Also, changing the return type of inherited methods is not allowed in java.

You are overwriting the int compareTo() Method of the Object class. There is no bool compareTo() to overwrite. It just doesn't exists.
When overwriting int compareTo(), you should return 0 if the objects are equal, a negative number if other is greater than this and a positive number if other is smaller than this

Related

Best way to compare different elements in equals() method for custom LinkedList

In my class below I have 2 strings and an equals method for my custom LinkedList. Now I know how to compare different objects in my equals method but say I wanted to compare name earlier in my programme before I compared number how would I type this in my equals() method. Below I tried an else if but I know this wont work as number will be comparing itself to another number when I only want it be comparing a name and the same later in my programme when I only want to compare a number and not a name, if that makes sense. Below is my class and what I tried for my equals method. Any help would be greatly appreciated :) P.S sorry class is called Id im not very good with stack overflow and could not get it to show in the code section
private String number;
private String name;
public Id(){
this.number = "";
this.name = "";
}
public Id(String number,String name){
this.number = number;
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public String getNumber(){
return this.number;
}
public void setNumber(String number){
this.number = number;
}
#Override
public boolean equals(Object o){
Id i = (Id)o;
if(this.number.equalsIgnoreCase(e.getNumber())){
return true;
}else if(this.name.equalsIgnoreCase(e.getName()))
return true;
else
return false;
'''
That equals method is broken.
It should not throw. In particular, if you attempt to ask it: Hey, are you equal to this apple, it should answer 'no'. Your code will 'answer' with a ClassCastException.
Your code will return true if the number is equal to the other number, __OR__ if the nameis equal to the othername. That's a bizarre definition. Surely it should be: Both numberandname` should be equal.
You can't override only equals, you must always override both equals and hashCode. If you fail to do this, using this object for example as keys in a HashMap results in utterly bizarre behaviour, where keys you add seem to just disappear into thin air.
If you want an equals method that only compares numbers and not the name, you can do that, you can write whatever you want. But, an equals method must be commutative, associative and adhere to identity. In other words, a.equals(a) must be true, if a.equals(b), then b.equals(a) must be true, and if a.equals(b) && b.equals(c), then a.equals(c) must be true. Again, the compiler won't stop you if you fail to adhere to these rules, but various classes rely on your equals method working like this, and if it doesn't, they'll act in bizarre ways.
If your goal really was: Any 2 IDs are equal if name matches name OR number matches number - then your method mostly does that, though the cast is wrong, you have to add:
if (this == o) return true;
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
Id other = (Id) o;
// carry on comparing members here

Can we use equals() method to compare all content in an object? [duplicate]

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

Why does my program not recognize two objects as equal when they have the same values? [duplicate]

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

Java collection set for user defined types with unique key strings

I was wondering is there a collection that I can use for a class Weapon where it's like a hash_set of Weapon objects, but I want to use a name field in my weapon class and make it so that no two Weapons with the same name can exist in the set. I want to compare inputs with strings of my Weapon set to see if it already exists, and if it does, don't put it in the set, but I don't want to search the entire map every single time. Also, it wouldn't be case sensitive.
In your equals(), check only the name field for equality.
public class Weapon {
private String name;
// ... other fields
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Weapon weapon = (Weapon) o;
return name.equalsIgnoreCase(weapon.name); //case-insensitive
}
#Override
public int hashCode() {
return name.toLowerCase().hashCode();
}
}
And then use your HashSet like so:-
public static void main(String[] args) {
Set<Weapon> weapons = new HashSet<>();
Weapon w1 = new Weapon();
// TODO set the name of the weapon or pass it in constructor
if (weapons.add(w1)) {
System.out.println("Weapon added");
} else {
System.out.println("Weapon with same name already exists");
}
}
We have to override equals() and hashcode() methods to return the same interger for name.
Below is the equals() and hashcode () contract:
See JavaDoc of java.lang.Object
In hashCode() it says:
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.
If you only override equals() and not hashCode() your class violates this contract.
This is also said in the JavaDoc of the equals() method:
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

TreeSet example

Why the 3rd object is not being added to the treeset here though it is a different one?
import java.util.*;
class Student implements Comparable<Student>{
public String fn,ln;
public Student(String fn,String ln){
this.fn=fn;
this.ln=ln;
}
//overiding equals
public boolean equals(Object o) {
if (!(o instanceof Student))
return false;
Student s=(Student) o;
if(this==s)
return true;
if(this.fn.equals(s.fn) && this.ln.equals(s.ln))
return true;
return false;
}
//overiding hashcode
public int hashCode() {
return fn.hashCode()+ln.hashCode();
}
//overiding compareTo
public int compareTo(Student o) {
return this.fn.compareTo(o.fn);
}
}
public class Practice {
public static void main(String[] args) {
Student st1=new Student("Girish","J");
Student st2=new Student("Master","M");
Student st3=new Student("Girish","Jay");
Set S=new TreeSet();
//adding 3 different student objects
System.out.println(S.add(st1));
System.out.println(S.add(st2));
System.out.println(S.add(st3));
Iterator sitr=S.iterator();
while(sitr.hasNext())
{
Student stu=(Student) sitr.next();
System.out.println(stu.fn+" "+stu.ln);
}
}
}
Output:
true
true
false
Girish J
Master M
Your comparator function only uses fn:
public int compareTo(Student o) {
return this.fn.compareTo(o.fn);
}
TreeSet only uses ordering comparisons - it doesn't use hashCode() and equals().
By this comparison, st1 and st3 are equal (s1.compareTo(s3) will return 0) therefore st3 isn't added to the set.
If you want to maintain the distinction, you should probably compare fn and then use ln if the fn values are the same:
public int compareTo(Student o) {
int fnResult = this.fn.compareTo(o.fn);
return fnResult == 0 ? ln.compareTo(o.ln) : fnResult;
}
Your observations are correct that TreeSet does not use .equals and .hashcode for comparison.
From the javadocs:
This is so because the Set interface is defined in terms of the equals operation, but a
TreeSet instance performs all element comparisons using its compareTo (or compare) method,
so two elements that are deemed equal by this method are, from the standpoint of the set,
equal.
Basically, they are saying that for TreeSet, equality is determined not through .equals, but through .compareTo on the Comparable interface. Note that .compareTo should always be in line with .equals, meaning that if a.equals(b), then a.compareTo(b) == 0.
This has to do with the fact that TreeSet is an implementation of SortedSet. As such, it needs .compareTo in order to determine order, since .equals is not enough in that case.
PS: If you do not want to implement Comparable (which sometimes you can't since you might not always control the code of the objets), you could always pass a Comparator to the TreeSet constructor.
Your comparison is only using the fn value...
public int compareTo(Student o) {
return this.fn.compareTo(o.fn);
}
Which fails for the 3rd Student because the first name is identical to the 1st Student
You need to adjust your code to compare both the fn and the ln values...
public int compareTo(Student o) {
int firstNameComparison = this.fn.compareTo(o.fn);
if (firstnameComparison != 0){
// the first names are different
return firstNameComparison;
}
else {
// the first names are the same, so compare the last name
return this.ln.compareTo(o.ln);
}
}
This code compares the fn values first. If they are identical, it then compares the ln values.

Categories