why equals method must have equality operator comparison - java

When we override equals method we write the code as below
public boolean equals(Object obj) {
//why do we need to use this piece of code
**if (obj == this) {
return true;
}**
// How does the above two lines help. Instead I do not want to have the == on an object comparison
//I prefer to remove the above 2 lines. Will it cause any issue?
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Person guest = (Person) obj;
return id == guest.id
&& (firstName == guest.firstName
|| (firstName != null && firstName.equals(guest.getFirstName())))
&& (lastName == guest.lastName
|| (lastName != null && lastName .equals(guest.getLastName())));
}

Performance improvement and as well as some logical sense.
If you remove that, there is no harm.
But, there is no meaning of checking the conditions on same instance if both are pointing to same instance.
They are always true if obj == this. Think.

What will you do if the two object references are pointing to the same object. There is no need to actually check the firstName and lastName inside them for equality once both are pointing to the same instance. The above code is to make your logic perform better when the two references are pointing to the same object. It's a clean, perfect and good practice code style which leverage the performance. Please don't remove it.

Since comparison of references is very effective and object definitely equals to itself comparison of references (if(obj == this)) seems to be a good practice.

== sign is used to measure the object reference.If the Object is being compared with itself,it should always be true.
There should be no wastage in terms of overhead in comparing of Object's own attributes as they would always be equal.
Attributes should be measured when the two Objects refer to different Objects in HEAP

Related

Common case for comparing two objects references

Beside checking if null (something == null) when do we use object reference comparisons in Java?
I can't think of any case to use object reference comparisons. For me that seems a little weird for a language abstracting all memory allocations.
Comparing singletons - singleton should has only one instance and could be checked for identity instead of equality.
Comparing enums (enums are singletons)
In some equals methods themselves like in (AbstractList):
public boolean equals(Object o) {
// Checking identity here you can avoid further comparison and improve performance.
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
It's faster than a full equals comparison.
The reasoning is simple, if two objects are the same, they must be equal. Therefore it's often used in equals implementations but also to compare enums as J-Alex already pointed out.
Comparing Object references with == is useful when we need to see if two values refer to the same object.
With object references, the use of == is generally limited to the following:
Checking a reference is null.
Comparing two enum values, because there is only one object for each enum constant.
Checking if two references are to the same object.
In addition, by default, Object#equals() uses only the == operator for comparisons. This method has to be overridden to really be useful:
public boolean equals(Object obj) {
return (this == obj); // Comparing object references by default
}

java.util.Objects.isNull vs object == null

As you know, java.util.Objects is
This class consists of static utility methods for operating on objects.
One of such methods is Objects.isNull().
My understanding is that Objects.isNull() would remove the chance of accidentally assigning a null value to object by omitting the second =.
However, the API Note states:
This method exists to be used as a Predicate, filter(Objects::isNull)
Would there be any reason/circumstance for which I should use object == null over Objects.isNull() in an if statement?
Should Objects.isNull() be confined to Predicates exclusively?
should use object == null over Objects.isNull() in a if statement?
If you look at the source code of IsNull method,
/* Returns true if the provided reference is null otherwise returns false.*/
public static boolean isNull(Object obj) {
return obj == null;
}
It is the same. There is no difference. So you can use it safely.
Objects.isNull is intended for use within Java 8 lambda filtering.
It's much easier and clearer to write:
.stream().filter(Objects::isNull)
than to write:
.stream().filter(x -> x == null).
Within an if statement, however, either will work. The use of == null is probably easier to read but in the end it will boil down to a style preference.
Look at the source:
public static boolean isNull(Object obj) {
return obj == null;
}
To check for null values, you can use:
Objects.isNull(myObject)
null == myObject // avoids assigning by typo
myObject == null // risk of typo
The fact that Objects.isNull is meant for Predicates does not prevent you from using it as above.
Would there be any reason/circumstance for which I should use object == null over Objects.isNull() in a if statement?
Yes, one reason is to keep the code simple. Within if statement object == null is clear and well known. It can not lead to any misbehavior if for example there is a typo.
My understanding is that Objects.isNull() would remove the chance of accidentally assigning a null value to object by omitting the second =.
If there is an if (object = null) {} with omitted = it will not compile or it will generate warning in case of Boolean object! Actually there is no reason to use Objects.isNull(object) over object == null within if statement. Here are the two variants side by side:
if (object == null) {
}
if (Objects.isNull(object)) {
}
Should Objects.isNull() be confined to Predicates exclusively?
It could be said yes, it is confined to Predicates exclusively, although there is no technical hurdle to use the Objects.isNull() everywhere.
From the public static boolean isNull(Object obj) method's javadoc:
#apiNoteThis method exists to be used as a java.util.function.Predicate, filter(Objects::isNull)
So if you use the method as not a predicate you are actually using a more complex and cumbersome expression compared to the simple object == null.
Here is a snippet to compare the benefit of Objects.isNull(object)
List<String> list = Arrays.asList("a", "b", null, "c", null);
// As ready-made predicate
long countNullsWithPredicate = list.stream().filter(Objects::isNull).count();
// Lambda
long countNullsWithLambda = list.stream().filter(object -> object == null).count();
// Reimplement the Objects::isNull predicate
long countNullsWithAnonymous = list.stream().filter(new Predicate<Object>() {
#Override
public boolean test(Object obj) {
return obj == null;
}
}).count();
Semantically there is no difference but for readability I prefer the following over whatever == null:
import static java.util.Objects.isNull;
// Other stuff...
if(isNull(whatever)) {
}

How do I create a hash code for an object with an OR in equals()?

For example, my class looks like the class below. In equals(), I've defined an equivalence where having one of two ids (or both) being equal results in the entire MyObject.equals() method returning true. Regardless of this being a good thing to do (I've abandoned this approach for a project), I'm curious if it is possible to define a hashCode() method that would work correct with the equals() method.
public class MyObject {
private UUID id;
private UUID secondaryId;
public MyObject(UUID id, UUID secondaryId) {
this.id = id;
this.secondaryId = secondaryId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyObject myObject = (MyObject) o;
// Both id and secondary Id are not null on both objects
if (id != null && myObject.id != null && secondaryId != null &&
myObject.secondaryId != null) {
return id.equals(myObject.id) &&
secondaryId.equals(myObject.secondaryId);
}
// Id is not null on both objects
else if (id != null && myObject.id != null) {
return id.equals(myObject.id);
}
// Secondary id is not null on both objects
else if (secondaryId != null && myObject.secondaryId != null) {
return secondaryId.equals(myObject.secondaryId);
}
return false;
}
#Override
public int hashCode() {
// TODO: How do write a hashcode that reflects the equals method?
}
}
From my little thought on the manner, it seems very hard to do because you could always be missing data. When you are attempting to match an object with both ids defined with an object with a single id defined I don't see how you would be able to know the truth. The best answer I could come up with would be that in the case where you always had one object with both ids defined, you could create a hash that was the combination of two 16-bit hashes for both keys that had a marker between them. So you would always look at the first 16 bits for the first id and then look at the next 16 bits for the second id. That said - I don't have a clue on how to write that approach in a language like Java.
TLDR; How would you write the hashCode() method?
You can't. There would be no way create a unique hash code for 2 different objects that both "equal" another object.
Quote from Wikipedia:
The general contract for overridden implementations of this method (hashCode()) is
that they behave in a way consistent with the same object's equals()
method: that a given object must consistently report the same hash
value (unless it is changed so that the new version is no longer
considered "equal" to the old), and that two objects which equals()
says are equal must report the same hash value.

In a Java method that takes an Object argument, how can you access fields that only exist for instances of specific classes?

-- Yes, this is a question asking for help regarding something that was assigned as homework. No, this is not me asking you to do my homework for me. The deadline was a half an hour ago; I literally can't change my submission. You'll just have to take my word for it. Moving on...
I know that testing for the type of objects is supposed to be unnecessary. When I was looking for details about 'instanceof', I found a half dozen threads where people responded only to tell the original poster that they were making a mistake if they had to test to find out what kind of object they were dealing with before processing it. Yes, I know, I would love to follow the conventions. Unfortunately, my professor asked for us to override the equals method of a class we defined, and specifically required an Object type parameter. If you see my code, you'll probably understand better:
public boolean equals(Course other){
if(!(other instanceof Course)){
return false;
} else if(other.name==this.name && other.days==this.days &&
other.start==this.start && other.end==this.end){
return true;
}
return false;
}
You can probably understand where I'm going with this. The 'other' parameter should be an Object type, but the program has a fit if I leave it as an Object and use the name / days / start / end fields. If I change it to Course, of course it works (no pun intended), but that would be a completely different method. The desired behavior is for all objects other than Course instances to make the method return false, and additionally, for mismatched data between Course instances to make it return false.
I'm sorry to all of you who know Java well enough to be frustrated by seeing questions like these.
If you want to override the "equals" method, you should use Object as a parameter, and thus you have to check for the object's type. Usually your own implementation would look like this:
#Override
public boolean equals(Object obj) {
if (obj == this)
return true; // object's references are identical
else if (!(obj instanceof Course))
return false;
Course that = (Course) obj;
return (this.name.equals(that.name)) && (this.days == that.days)
&& (this.start.equals(that.start)) && (this.end.equals(that.end));
}
Of course you should override "hashCode" as well, using the same significant fields.
Instead, you overloaded the method with your own parameter of type Course. So if you call myobject.equals(anotherObject) and anotherObject is not of type Course, your "equals" method will never be called, instead the Object#equals method will be called, which does only the following: return this == obj.
The reason why overloading the "equals" method is not enough is the necessity to overload "hashCode" as well, which takes no parameters and thus cannot be overloaded.
If you write your own implementation of boolean equals(Object), you must also implement int hashCode()
Both methods should use the same significant fields for "hashCode" and "equals".
If a.equals(b) == true than the following must be also true: a.hashCode() == b.hashCode()
Also if a.hashCode() != b.hashCode() then a.equals(b) == false
The last point is the main reason why you should not just overload "equals" with your own type:
Course c1 = new Course("myname");
Course c2 = new Course("myname");
c1.equals(c2); // true
c1.hashCode() == c2.hashCode(); // false
You can cast Object as Course:
Course course = (Course)object;
Then do all the comparisons on the course object. Obviously, still do the instanceof check before casting to avoid a ClassCastException.
You're trying to cast it:
Cource c = (Course)other;
Your code :
public boolean equals(Course other){
if(!(other instanceof Course)){ <-- other can only be Course here
return false;
} else if(other.name==this.name && other.days==this.days &&
other.start==this.start && other.end==this.end){
return true;
}
return false;
}
Correct code :
public boolean equals(Object other){
if(!(other instanceof Course)){
return false;
} else{
Course c = (Course) other;
if(c.name==this.name && c.days==this.days &&
c.start==this.start && c.end==this.end){
return true;
}
}
}
return false;
}

Would a Java HashSet<String>'s contains() method test equality of the strings or object identity?

Let's say I have this code in Java:
HashSet<String> wordSet = new HashSet<String>();
String a = "hello";
String b = "hello";
wordSet.add(a);
Would wordSet.contains(b); return true or false? From what I understand, a and b refer to different objects even though their values are the same. So contains() should return false. However, when I run this code, it returns true. Will it always return true no matter where String object b is coming from as long as b contains the value "hello"? Am I guaranteed this always? If not, when am I not guaranteed this? And what if I wanted to do something similar with objects other than Strings?
It uses equals() to compare the data. Below is from the javadoc for Set
adds the specified element e to this set if the set contains no
element e2 such that (e==null ? e2==null : e.equals(e2)).
The equals() method for String does a character by character comparison. From the javadoc for String
The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object
Actually, HashSet does neither.
Its implementation uses a HashMap, and here's the relevant code that determines if the set contains() (actually it's inside HashMap's getEntry() method):
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
which:
requires the hashes to equal, and
requires either object equality or equals() returns true
The answer is "yes": wordSet.contains(b) will always return true
Actually, both a and b refer to the same object, because string literals in Java are automatically interned.
Two things:
A set would be pretty useless unless it called the equals() method to determine equality. wordset.contains(b) will return true because a.equals(b) == true.
You cannot be totally sure that a and b are pointing to different objects. Checkout String.intern() for more details.
Ultimately contains will check for equals method rather then its object id validation for contains method. Hence equals method will be called for contains call.
This is the call structure of contains method.
private transient HashMap<E,Object> map;
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
Equality. In your example, contains() returns true, because the HashSet checks a.equals( b ).

Categories

Resources