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
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.
I find that a dangerous choice from junit to call
assetEquals
but not useObject.equals
... this lead to "unexpected" behavior like this one.– AxelH
Sep 6 '18 at 10:22