Why are empty collections of different type equal?

JavaCollectionsTestng

Java Problem Overview


What is mechanism below that makes equal different types?

import static org.testng.Assert.assertEquals;

@Test
public void whyThisIsEqual() {
    assertEquals(new HashSet<>(), new ArrayList<>());
}

Java Solutions


Solution 1 - Java

The assertEquals(Collection<?> actual, Collection<?> expected) documentation says:

> Asserts that two collections contain the same elements in the same order. If they do not, an AssertionError is thrown.

Thus the content of the collections will be compared which, in case both the collections are empty, are equal.

Solution 2 - Java

They are not...

System.out.println(new HashSet<>().equals(new ArrayList<>())); // false

This is specific to testng assertEquals

Looking at the documentation of that method it says:

> Asserts that two collections contain the same elements in the same order.

And this is ridiculous to me, a Set does not have an order, per-se.

Set<String> set = new HashSet<>();
set.add("hello");
set.add("from");
set.add("jug");

System.out.println(set); // [from, hello, jug]

IntStream.range(0, 1000).mapToObj(x -> x + "").forEachOrdered(set::add);
IntStream.range(0, 1000).mapToObj(x -> x + "").forEachOrdered(set::remove);

System.out.println(set); // [jug, hello, from]

So comparing these against a Collection at some particular point in time would yield interesting results.

Even worse, java-9 Set::of methods implement a randomization internally, so the order (or not the order) will be different from run to run.

Solution 3 - Java

Testng calls through to a method implemented this way.

  public static void assertEquals(Collection<?> actual, Collection<?> expected, String message) {
    if (actual == expected) {
      return;
    }

    if (actual == null || expected == null) {
      if (message != null) {
        fail(message);
      } else {
        fail("Collections not equal: expected: " + expected + " and actual: " + actual);
      }
    }

    assertEquals(
        actual.size(),
        expected.size(),
        (message == null ? "" : message + ": ") + "lists don't have the same size");

    Iterator<?> actIt = actual.iterator();
    Iterator<?> expIt = expected.iterator();
    int i = -1;
    while (actIt.hasNext() && expIt.hasNext()) {
      i++;
      Object e = expIt.next();
      Object a = actIt.next();
      String explanation = "Lists differ at element [" + i + "]: " + e + " != " + a;
      String errorMessage = message == null ? explanation : message + ": " + explanation;
      assertEqualsImpl(a, e, errorMessage);
    }
  }

This is trying to be helpful but is poor for a number of reasons.

Two equals collections can appear to be different.

Set<Integer> a = new HashSet<>();
a.add(82);
a.add(100);
System.err.println(a);
Set<Integer> b = new HashSet<>();
for (int i = 82; i <= 100; i++)
    b.add(i);
for (int i = 83; i <= 99; i++)
    b.remove(i);
System.err.println(b);
System.err.println("a.equals(b) && b.equals(a) is " + (a.equals(b) && b.equals(a)));
assertEquals(a, b, "a <=> b");

and

Set<Integer> a = new HashSet<>();
a.add(100);
a.add(82);
System.err.println(a);
Set<Integer> b = new HashSet<>(32);
b.add(100);
b.add(82);
System.err.println(b);
System.err.println("a.equals(b) && b.equals(a) is " + (a.equals(b) && b.equals(a)));
assertEquals(a, b, "a <=> b");

prints

[82, 100]
[100, 82]
a.equals(b) && b.equals(a) is true
Exception in thread "main" java.lang.AssertionError: a <=> b: Lists differ at element [0]: 100 != 82
	at ....

Two collections can be the same or different depending on how they are compared.

assertEquals(a, (Iterable) b); // passes

assertEquals(a, (Object) b); // passes

assertEquals(Arrays.asList(a), Arrays.asList(b)); // passes

Solution 4 - Java

Because for collection only content is compared, not collection type.

Rationale behind it is that often some subclass of collection is returned from tested method and is irrelevant what exactly subclass is used.

Solution 5 - Java

When I run below code the condition is false.

if(	(new HashSet<>()).equals(new ArrayList<>())){
    		System.out.println("They are equal");
    	}

Hence for assertEquals, it is true that it only checks elements and its order for equality. But for equals it is false.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBartekView Question on Stackoverflow
Solution 1 - JavaS.K.View Answer on Stackoverflow
Solution 2 - JavaEugeneView Answer on Stackoverflow
Solution 3 - JavaPeter LawreyView Answer on Stackoverflow
Solution 4 - JavatalexView Answer on Stackoverflow
Solution 5 - JavaRajView Answer on Stackoverflow