2

I am having a weird issue which I am not being able to understand why this happens.

I have this equals method implemented in a DoublyLinkedList generic class:

@Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (getClass() != obj.getClass() || obj == null) {
            return false;
        }

        DoublyLinkedList<E> other = (DoublyLinkedList<E>) obj;

        if (this.size != other.size) {
            return false;
        }

        Iterator<E> iterator = this.iterator();
        Iterator<E> otherIterator = other.iterator();
        while(iterator.hasNext()){       
            if(iterator.next() != otherIterator.next()){
                return false;
            }
        }
        return true;
    }

Testing this method, in a unit test, like this:

@Test
public void testEquals() {
    System.out.println("equals");
    DoublyLinkedList <String> instance1 = new DoublyLinkedList <>(), instance2 = new DoublyLinkedList <>();

    instance1.addLast("Xpto");
    instance1.addLast("Ypto");
    instance1.addLast("Zpto");

    instance2.addLast("Xpto");
    assertFalse("Lists should not be equal", (instance1.equals(instance2)));
    assertFalse("Lists should not be equal", (instance2.equals(instance1)));        
    instance2.addLast("Ypto");
    assertFalse("Lists should not be equal", (instance1.equals(instance2)));
    assertFalse("Lists should not be equal", (instance2.equals(instance1)));
    instance2.addLast("Zpto");
    assertTrue("Lists should be equal", (instance1.equals(instance2)));
    assertTrue("Lists should be equal", (instance2.equals(instance1)));       
}

Gives me that the test passes. However, why does this happen, if I am using !=, instead of equals to compare each iterator's instance, in the first code? Shouldn't it compare the references, and thus fail?

Thanks in advance!

Diogo Santos
  • 780
  • 4
  • 17
  • 1
    You are adding `String` literals to the list. The `String` class takes a shortcut and has two of the same Strings point to the same memory address so `==` and `!=` works – GBlodgett Oct 05 '18 at 18:29
  • 1
    Read the last part of this answer: https://stackoverflow.com/a/513839/8534008 – GBlodgett Oct 05 '18 at 18:31
  • String literals in Java are **interned**. This is defined in the JLS (Java Language Specification). – Zabuzard Oct 05 '18 at 19:36

2 Answers2

2

Java is interning (or caching) certain references on your behalf. Specifically, if you enter Strings as your type, you're going to run into some curious String interning behavior, in that suddenly, your lists have the same reference of String. This is the only way that == would work on a reference at all - if it were somehow interned or cached and could be referenced.

This is trivial to defeat; if you use values which do not have the ability to be interned or cached, then you'll observe that your test fails.

For example, new BigInteger("100") and new BigInteger("100") are not the same location in memory, and if you put both of those in your list and attempt to compare equivalence, you'll get false.

Makoto
  • 104,088
  • 27
  • 192
  • 230
1

That's because they are pointing to the same location in memory.

See: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#d5e1634

Grant Foster
  • 722
  • 2
  • 11
  • 21