Why are empty collections of different type equal?

Why are empty collections of different type equal?



What is mechanism below that makes equal different types?


import static org.testng.Assert.assertEquals;


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




5 Answers
5



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


assertEquals(Collection<?> actual, Collection<?> expected)



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.



They are not...


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



This is specific to testng assertEquals


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


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.


Collection



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.


java-9


Set::of






I find that a dangerous choice from junit to call assetEquals but not use Object.equals... this lead to "unexpected" behavior like this one.

– AxelH
Sep 6 '18 at 10:22



assetEquals


Object.equals






@AxelH indeed, I was really not expecting this

– Eugene
Sep 6 '18 at 10:23






Junit throws an AssertionError here. It is testng which has this behaviour. (I see you edited your answer right when I posted this comment)

– marstran
Sep 6 '18 at 10:24



AssertionError






@marstran see another edit, I find this a very very weird

– Eugene
Sep 6 '18 at 10:34






@AxelH & Eugene, I agree, this method is misnamed. This should be named something like assertEqualContentsOrdered. It would be useful for comparing a list and a set, in the case that you know that the set is ordered. (There is already assertEqualsNoOrder but it only takes arrays.)

– Stuart Marks
Sep 6 '18 at 16:37



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



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






It's calling this overload: github.com/cbeust/testng/blob/master/src/main/java/org/testng/…

– marstran
Sep 6 '18 at 10:27






@marstran ouch, so the type of the reference rather than the object changes the behaviour.

– Peter Lawrey
Sep 6 '18 at 10:30






Yep. The overload should have another name imho. Like assertSameElements or something.

– marstran
Sep 6 '18 at 10:35


assertSameElements






@marstran or assertEquals(Object,Object) should check at runtime but even then it's really only good for List not Set

– Peter Lawrey
Sep 6 '18 at 10:45







@AxelH This function is bad other reasons, but that fact that it considers null == null is perfectly consistent with the rest of the language.

– Todd Sewell
Sep 6 '18 at 11:53


null == null



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.



When I run below code the condition is false.


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.


assertEquals


equals



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)