Java 學習記錄70 — Sets & HashSet — 2/5

what does “this” in the below code actually mean?

It compares the memory addresses for both the object passed as parameter and the one you call equals from. If they are in the same memory address, they are obviously the same object.

Hi Tim,

I was running a quick test to make sure I understand the .equals() method and the == operator, but I’m confused by my result.

If I have two strings with the same value but assigned to different variables, I would expect comparing with == to return false since diff objects and comparing with .equals() to return true since same value, but both seem to return true.

Am I missing something? Thanks!

Example:

String test = "Test";String test2 = "Test";test == test2; // returns true unexpectedlytest.equals(test2); // returns true as expected

The == really has to do with the way Java is creating the constants.

Consider this code

String obj1 = new String("xyz");String obj2 = new String("xyz");if (obj1 == obj2)
System.out.println("obj1==obj2 is TRUE");
else
System.out.println("obj1==obj2 is FALSE");

If you run it, you will see it returns false.

We used new here to be sure a new String was created.

When you just assigned test the value of “Test” and then test2, the Java compiler decided to use only one copy of memory for “Test”

And this == returned true. == is meant to only return true if the object that are pointing to in memory are equal.

Add this code below the above code.

obj2 = obj1;
if(obj1 == obj2)
System.out.println("obj1==obj2 is TRUE");
else
System.out.println("obj1==obj2 is FALSE");

Note how now we have got obj2 pointing to obj1 the value returned is true.

.equals, on the other hands, does not look at memory locations, it looks at what the contents of the strings are and returns true or false if they are equal.

import java.util.HashSet;
import java.util.Set;
public class HeavenlyBody {
private final String name;
private final double orbitalPeriod;
private final Set<HeavenlyBody> satellites;
public HeavenlyBody(String name, double orbitalPeriod) {
this.name = name;
this.orbitalPeriod = orbitalPeriod;
this.satellites = new HashSet<>();
} public String getName() {
return name;
}
public double getOrbitalPeriod() {
return orbitalPeriod;
}
public boolean addMoon(HeavenlyBody moon) {
return this.satellites.add(moon);
}
public Set<HeavenlyBody> getSatellites() {
return new HashSet<>(this.satellites);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
System.out.println("obj.getClass() is " + obj.getClass());
System.out.println("this.getClass() is " + this.getClass());
if ((obj == null) || obj.getClass() != this.getClass()) {
System.out.println("obj is null or not equal");
return false;
}
String objName = ((HeavenlyBody) obj).getName();
return this.name.equals(objName);
}
}

Uranus: 30660.0

Venus: 225.0

Pluto: 248.0

Saturn: 10759.0

Mars: 1.3

Jupiter: 4332.0

Earth: 365.0

Mercury: 88.0

Neptune: 165.0

Pluto: 842.0

Overriding equals and hashCode in Java — 039

Are two Java objects with same hashcodes not necessarily equal?

import java.util.HashSet;
import java.util.Set;
public final class HeavenlyBody {
private final String name;
private final double orbitalPeriod;
private final Set<HeavenlyBody> satellites;
public HeavenlyBody(String name, double orbitalPeriod) {
this.name = name;
this.orbitalPeriod = orbitalPeriod;
this.satellites = new HashSet<>();
}
public String getName() {
return name;
}
public double getOrbitalPeriod() {
return orbitalPeriod;
}
public boolean addMoon(HeavenlyBody moon) {
return this.satellites.add(moon);
}
public Set<HeavenlyBody> getSatellites() {
return new HashSet<>(this.satellites);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
System.out.println("equals called");
System.out.println("obj.getClass() is " + obj.getClass());
System.out.println("this.getClass() is " + this.getClass());
if ((obj == null) || (obj.getClass() != this.getClass())) {
System.out.println("obj is null or not equal!");
return false;
}
String objName = ((HeavenlyBody) obj).getName();
return this.name.equals(objName);
}
@Override
public int hashCode() {
System.out.println("hashcode called");
return this.name.hashCode() + 57;
}
}

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

hashcode called

equals called

obj.getClass() is class HeavenlyBody

this.getClass() is class HeavenlyBody

Uranus: 30660.0

Mercury: 88.0

Pluto: 248.0

Earth: 365.0

Jupiter: 4332.0

Mars: 1.3

Neptune: 165.0

Saturn: 10759.0

Venus: 225.0

Working With hashcode() and equals()

A collision is when two different objects inadvertently return the same hash. HeavenlyBody was using the hashcode returned from the instance variable “name”. Now, the longer the name is, the less likely of a collision. If two DIFFERENT strings just so happen to equate to the same hashcode (which is an integer), then adding 57 will just take that integer and add 57 to both, resulting in (again) the same number.
The only advantage of adding 57 here is to distinguish between a HeavenlyBody object and a String. Without using 57, if a HeavenlyBody object was inadvertently compared to a String which contained the same value as the instance variable “name” from HeavenlyBody, then yes, adding 57 would make them different.

However, I don’t think this is necessary, so long as the programmer doesn’t make this mistake, which wouldn’t even make sense. Like, what point is there in comparing a String to a user defined object here?

Actually, adding 57 here allows for a false-positive result. If the programmer accidentally compared a String to a non-String, it would put these two different objects in different buckets.

On the other hand, if they were in the same bucket, then there would actually be a chance to catch this mistake.

I think Tim, in this case, has done a pretty big disservice to beginners by doing something unnecessary AND not even thoroughly discussing why he did what he did. If he wanted to introduce a complex concept like this to beginners, he should have at LEAST said not to over-think this and that he will be discussing it greater depth in later chapters.

Simply saying that adding a number to a hash will result in lower collisions also doesn’t really help unless told WHY. Simply repeating a fact without giving an explanation comes off a little self-aggrandizing.

The method equals() is defined in the Object class. It is used to compare values for equality and returns a boolean value (true or false). If you don’t override this method in your class, it compares if the references of the objects passed as arguments are equal or not. E.g., in the String class the equals() method has been override and in this case equals() compares the string with the object passed as parameter.

The method compareTo() is defined in the Comparable interface. If your class objects have a natural order you can implement the Comparable<T> interface and define this method. It is used by methods like Collections.sort() to compare the objects during the ordering process. That is, A.equals(B) compares if object A is equal (then return 0), greater than (then return a positive integer) or less than (then return a negative integer) object B. E.g., the class Integer implements Comparable and compareTo() method is defined because the integer has a natural order ( -n, …, -2, -1, 0, 1, 2, …, n). The returned values are useful to order the objects in a collection.

But what’s happens if your application needs to order a collection of objects in different ways. E.g., you have a class Person with two fields (name and age) but sometimes your application must order the objects by name and sometimes by age. Then you need to use the method compare(), defined in the Comparator interface. The typical use is define one or more classes that implements this (e.g. we can define two classes that implements Comparator, one that compare two Person objects using the age and other that compare two Person objects using the name). Once this comparators are defined, those can be passed to method such as sort() then the sort() method uses the comparator to compare and order the objects of the collection.

When overriding the equals() method in the HeavenlyBody class, we

were careful to make sure that it would not return true if a HeavenlyBody

was compared to a subclass of itself.

We did that to demonstrate that method, but it was actually

unnecessary in the HeavenlyBody class.

The mini challenge is just a question: why was it unnecessary?

The HeavenlyBody class is declared final, so cannot be subclassed.

The Java String class is also final, which is why it can safely

use the instanceof method without having to worry about

comparisons with a subclass.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store