Comparing Collections in Java

Donald Raab
5 min readMay 2, 2024

--

Learn when to use List, Set, Bag or Map if you don’t have a Bag.

Photo by gemma on Unsplash

Test Infected and Happy!

I love writing executable code examples in unit tests. Using JUnit Assertions is so much more interesting and sometimes more challenging than just writing a main() method with some System.out.println() methods to experiment. Writing tests gives me confidence that my code works today, and confidence tomorrow that the code I wrote still works as written. If it doesn’t work, the test will fail and tell me.

Two of the most common JUnit 5 Assertions that I use are assertEquals and assertNotEquals. When these methods fail, they provide useful messages explaining what the difference is between the two objects being compared based on their toString result.

These two Assertions are very useful when it comes to comparing two Collections in Java. Developers who use the Java Collection Framework have three basic Collection types that they can use to compare for equality. These types are java.util.List, java.util.Set, and java.util.Map. In Eclipse Collections, there is a fourth type named Bag.

Comparing Collections for Equality

A Collection is equal to another Collection of the same type. So a List can be equal to a List, a Set can be equal to a Set, and a Bag can be equal to a Bag. SortedSet can be equal to another Set (which may be a SortedSet). SortedBag can be equal to a Bag (which may be a SortedBag). We may need to convert a Collection to the type we want to use for the equality test we are interested in.

Collection Equality Examples

For the test examples, I will use a simple Fruit enum with APPLE, BANANA, and CHERRY.

enum Fruit
{
APPLE, BANANA, CHERRY;
}

The following are some examples of testing equality for a List.

@Test
public void listEquality()
{
MutableList<Fruit> list =
Lists.mutable.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Equality of List is based on Type, Size, Contents, and Order
// List allows duplicates
// Order DOES impact the equality of a List
Assertions.assertEquals(
list,
List.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY));

Assertions.assertNotEquals(
list,
List.of(Fruit.APPLE, Fruit.BANANA, Fruit.CHERRY));
Assertions.assertNotEquals(
list,
List.of(Fruit.CHERRY, Fruit.BANANA, Fruit.BANANA, Fruit.APPLE));
}

The following are some examples of testing equality for a Set.

@Test
public void setEquality()
{
MutableSet<Fruit> set =
Sets.mutable.of(Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Equality of Set is based on Type, Size and Contents.
// Set allows unique elements only.
// Order DOES NOT impact the equality of a Set
Assertions.assertEquals(
set,
Sets.mutable.of(Fruit.APPLE, Fruit.BANANA, Fruit.CHERRY));
Assertions.assertEquals(
set,
Sets.mutable.of(Fruit.CHERRY, Fruit.BANANA, Fruit.APPLE));

Assertions.assertNotEquals(
set,
Sets.mutable.of(Fruit.APPLE, Fruit.BANANA));
Assertions.assertNotEquals(
set,
Sets.mutable.of(Fruit.BANANA, Fruit.CHERRY));
}

The following are some examples of testing equality for a Bag.

@Test
public void bagEquality()
{
MutableBag<Fruit> bag =
Bags.mutable.of(Fruit.APPLE, Fruit.BANANA, Fruit.BANANA, Fruit.CHERRY);

// Equality of Bag is based on Type, Size and Contents
// Bag allows duplicates
// Order DOES NOT impact the equality of a Bag
Assertions.assertEquals(
bag,
Bags.mutable.of(Fruit.BANANA, Fruit.APPLE, Fruit.BANANA, Fruit.CHERRY));
Assertions.assertEquals(
bag,
Bags.mutable.of(Fruit.CHERRY, Fruit.BANANA, Fruit.APPLE, Fruit.BANANA));

Assertions.assertNotEquals(
bag,
Bags.mutable.of(Fruit.APPLE, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY));
Assertions.assertNotEquals(
bag,
Bags.mutable.of(Fruit.APPLE, Fruit.BANANA, Fruit.CHERRY));
}

Converting Collections to Test for Equality

Sometimes we will need to convert a Collection from one type to another in order to apply the equality comparison we are most interested in. The following table lists several types of equality tests we may want to perform on a Collection of items, with the best Collection type to use in those circumstances.

The Java Collection Framework does not have a Bag type, so a Map type would need to be used to simulate a Bag.

Converting to a sorted List for Equality Test

Using Eclipse Collections MutableList as a source type, we can convert the List to a sorted List using toSortedList. There is no SortedList type, so sorting doesn’t actually change the type, but toSortedList will create a sorted copy of the List, so the source List remains unchanged.

The following code copies a MutableList<Fruit> to a new MutableList<Fruit> and sorts it in natural order using toSortedList.

@Test
public void convertToSortedListForEquality()
{
MutableList<Fruit> list =
Lists.mutable.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Sorting the List we can more predictably compare the contents
List<Fruit> sortedList = list.toSortedList();

Assertions.assertEquals(
sortedList,
List.of(Fruit.APPLE, Fruit.BANANA, Fruit.BANANA, Fruit.CHERRY));

Assertions.assertNotEquals(
sortedList,
List.of(Fruit.CHERRY, Fruit.BANANA, Fruit.CHERRY, Fruit.APPLE));
}

Converting to a Set for Equality Test

Using Eclipse Collections MutableList as a source type, we can convert the List to a Set using toSet.

@Test
public void convertToSetForEquality()
{
MutableList<Fruit> list =
Lists.mutable.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Using a Set we can test unique contents without caring for order
Set<Fruit> set = list.toSet();

Assertions.assertEquals(
set,
Set.of(Fruit.CHERRY, Fruit.BANANA, Fruit.APPLE));
Assertions.assertEquals(
set,
Set.of(Fruit.APPLE, Fruit.BANANA, Fruit.CHERRY));

Assertions.assertNotEquals(
set,
Set.of(Fruit.APPLE, Fruit.BANANA));
}

Converting to a Bag for Equality Test

Using Eclipse Collections MutableList as a source type, we can convert the List to a Bag using toBag.

@Test
public void convertToBagForEquality()
{
MutableList<Fruit> list =
Lists.mutable.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Using a Bag we can test size and contents without caring about order
Bag<Fruit> bag = list.toBag();

Assertions.assertEquals(
bag,
Bags.mutable.of(Fruit.CHERRY, Fruit.BANANA, Fruit.BANANA, Fruit.APPLE));

Assertions.assertNotEquals(
bag,
Bags.mutable.of(Fruit.CHERRY, Fruit.BANANA, Fruit.CHERRY, Fruit.APPLE));
}

I often find Bag is the most useful Collection type to use for equality testing, unless I care explicitly about the order.

What if I don’t have a Bag?

My recommendation would be to download Eclipse Collections and get yourself a reusable Bag for your Fruit! 🍎🍌🍒

If you can’t afford a Bag type in your code base for some reason, you can convert the List to a Map as follows. It’s just a bit more verbose.

@Test
public void convertToMapForEquality()
{
List<Fruit> list =
List.of(Fruit.BANANA, Fruit.BANANA, Fruit.APPLE, Fruit.CHERRY);

// Converting to Map we can also test size and contents without order
Map<Fruit, Long> simulatedBag = list.stream()
.collect(Collectors.groupingBy(each -> each, Collectors.counting()));

Assertions.assertEquals(
simulatedBag,
Map.of(Fruit.BANANA, 2L, Fruit.CHERRY, 1L, Fruit.APPLE, 1L));

Assertions.assertNotEquals(
simulatedBag,
Map.of(Fruit.BANANA, 1L, Fruit.CHERRY, 2L, Fruit.APPLE, 1L));
}

Summary

Comparing two Collections using equals tests a bunch of things simultaneously. Using equals will test the type, the size, the contents, and sometimes the order of the contents, depending on the equals contract of a particular type. Knowing when to use equals with two instances of List, Set, Bag, or Map can help you write more concise and meaningful assertions. Using equals is better than just testing size, and less lines of code if you have to call contains multiple times. Sometimes it helps to convert a particular Collection to another type to test for equality, depending on what you are looking to test.

Thank you for reading, and I hope you find this blog useful!

I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions.

--

--

Donald Raab

Java Champion. Creator of the Eclipse Collections OSS Java library (https://github.com/eclipse/eclipse-collections). Inspired by Smalltalk. Opinions are my own.