[Java] [Java] Is it not necessary to check identity in the equals() method implementation?

5 minute read

Introduction

Thank you for visiting. For Java’s equals() method, I compared the execution times with and without the following implementations that immediately return true if they are the same.

// Check equality at the beginning of equals()
if (this == obj){
return true;
}

Kisatsu (I’m sorry for being long)

I am currently training as a SIer new graduate SE. It’s a little boring because it’s premised on Java inexperienced people, but it’s fun. (I feel a sense of crisis that I have to study for myself even in a short time…)

Now, in the training, we are doing a development exercise. It is like a project experience such as performing unit test, integration test, internal design document (component specification), and coding based on the created external design document.

In the internal design, a flowchart of the method is created so that it can be coded based on the field name and method name already decided. When you start working on a DTO specification, ** “Override the equals() method” ** There was. Oh, this is the place I did at the seminar.

equals() method override

The equals() method is defined in Object class as follows.

// Excerpt from Java 11 source code
public boolean equals(Object obj) {
return (this == obj);
}

This only returns the identity, so you need to implement it properly in your own class. Since the equivalence in this case is determined by the specification, for example, in my own class Person, even if the age fields are different, even if name is equivalent, it is free to judge (if I think so). Capture.PNG

And here is the main subject of this article. The equals() method first checks for equality.

// Check equality at the beginning of equals() (repost)
if (this == obj){
return true;
}

That is, in the case of a.equals(a), it returns true immediately. This is not required, but it is customary to implement it as it affects performance, says Eclipse’s auto-generation. Without this, we need to determine if the field of a is equal to the field of a.

What is this process?

I made a flow chart for the equals() method and submitted it to the PM instructor, and received the following comment from the corresponding identity check processing part. ** “What is this process?” ** When I implemented equals(), I thought it was a process to include it, so I did not think it would be stuck. Even if I put it in, even if I know the concept of identity check itself and point out that “this time it’s small, I don’t need strict processing up to this point”, it would be overwhelming, but “I do not know such processing, even in the field It’s meaningless because I haven’t seen it and the returned results are the same.” Of course, I’m a student until recently, and I don’t know anything about the scene, so maybe there is no identity check in the world…? I became suspicious of this, and I thought that this would just be treated as a fool who wrote redundant logic, so I wrote this article. For the time being, I will win if I can realize the benefits of execution time (forced).

environment

Windows10 Eclipse

Method

Use the following Person.java. Originally, we had to override the equals() method of the Object class, but for comparison, we created equalsWithSelfCheck() and an equalsWithoutSelfCheck() method with no equality check and compared them. Both are based on the automatically generated equals() method of Eclipse.

Person.java


public class Person {

private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public boolean equalsWithoutSelfCheck(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

public boolean equalsWithSelfCheck(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

In addition, the following conditions are the same in Main and the difference in execution time is output. The measurement was repeated 10 times.

Main.java


public class Main {

public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
Person person = new Person("Taro", 20);

// A list containing all the same persons
for(int i = 0; i <10000000; i++) {
list.add(person);
}

for (int i = 0; i <10; i++) {
long time1 = 0;
long time2 = 0;

for (Person p :list) {
long start = System.nanoTime();
person.equalsWithoutSelfCheck(p);
long end = System.nanoTime();
time1 = time1 + end-start;
}

for (Person p :list) {
long start = System.nanoTime();
person.equalsWithSelfCheck(p);
long end = System.nanoTime();
time2 = time2 + end-start;
}

System.out.println("Execution time difference:" + (time1-time2) * 0.000001 + "ms");
}
}
}


result

Execution time difference: 7.82333999999999995 ms
Execution time difference: 5.2443 ms
Execution time difference: 3.8985 ms
Execution time difference: 4.9727 milliseconds
Execution time difference: 5.5971 ms
Execution time difference: 2.7468 ms
Execution time difference: 10.9687 ms
Execution time difference: 5.1853 ms
Execution time difference: 5.4607 ms
Execution time difference: 3.6744 ms

In all cases, the method including the identity check resulted in faster results. There are some variations. The order of execution may be the cause, so I tried changing the order, but in both cases the difference in execution time was negative, and the same conclusion was reached.

By the way

I also tried the case where the contents of the list are equal but not identical, as shown below.

// A list where all elements are equal (not identical)
for(int i = 0; i <10000000; i++) {
list.add(new Person("Taro", 20));
}

As a result, there was no average execution time difference as shown below.

Execution time difference: 3.5603 ms
Execution time difference: -0.223 ms
Execution time difference: 1.0935 milliseconds
Execution time difference: 0.5618 ms
Execution time difference: -0.006999999999999999 milliseconds
Execution time difference: -1.4681 ms
Execution time difference: -0.8628 ms
Execution time difference: 1.5103 ms
Execution time difference: -1.99932999999999998 ms
Execution time difference: 0.3394 ms

From this, it was confirmed that the equality check in the equals() method contributes sufficiently to the performance.

Conclusion

It was confirmed that the equality check in the equals() method contributed to the performance sufficiently, and it was found to be a useful implementation. (My win (?))

at the end

Until the end Thank you for reading. If you have any questions, please point out and give us guidance.