Comparing Elements: equals vs ==

Our generic DynamicArray compiles and we can use it. Let’s put a student on the roster and then check that the student is there:

DynamicArray<Student> roster = new DynamicArray<>();
roster.add(new Student(1, "Ada", 3.9));

boolean here = roster.contains(new Student(1, "Ada", 3.9));
System.out.println(here);  // false (!?)

We added student #1, then immediately asked whether student #1 is on the roster, and we got back false.

The == operator compares references, not contents

The trouble is in indexOf, which contains calls:

if (arr[i] == value) { ... }

For int, == asks “are these the same number?”, which is exactly what we wanted. For objects, == asks a different question: “are these the same object?” That is, do both sides point to the very same thing in memory.

In the snippet above there are two separate Student objects. We created one with new and added it, then created another with new and passed it to contains. They have identical fields, but they are two distinct objects, so == says they are not the same, and the search never matches.

What we actually mean is “are these the same student?” That is a question about their contents, their ID, not about their identity, their location in memory. For that, Java uses the .equals method, not ==.

Use .equals to compare contents

The fix is to call .equals instead of ==:

public int indexOf(T value) {
  for (int i = 0; i < size; i++) {
    if (arr[i].equals(value)) {  // was ==
      return i;
    }
  }
  return -1;
}

This compiles and runs. Every object in Java has an .equals method, even our Student, which we never gave one. We will see where it comes from in the next section. For now, all we need is that calling .equals is always legal.

Notice that we changed one line and fixed the comparison for three operations. When we built the dynamic array, we pulled the search out of contains and remove into a single indexOf, and rewrote both of them to call it. The comparison now lives in one place, so there is one place to fix it. If we had left the search copied into contains and remove, we would have to make the same edit in two places, and we might miss one.

The bug is still there

Let’s run our check again:

roster.add(new Student(1, "Ada", 3.9));
roster.contains(new Student(1, "Ada", 3.9));  // still false!

We switched from == to .equals, which is the right method to call, and the answer did not change. The reason is that Student’s .equals currently behaves exactly like ==. It still compares references.

So the bug moved. It is no longer in our search code, which now asks the right question. It is in Student, which does not yet define what it means for two students to be equal.