In this post we will show how to find common objects in list collections by using an intersect
method in Groovy.
We will also show how to remove common objects from two lists in Groovy by using a removeAll
method.
Both operations will require an algorithm to compare complex objects. We will show how to do it in Java and Groovy with Grails domain objects.
With simple objects like Strings
the intersection of lists works right out of the box.
Here is a simple Groovy unit test example showing how to find common String objects within two collections:
void "should find Foo"() { setup: ArrayList list1 = new ArrayList() list1.add("Foo") list1.add("Bar") ArrayList list2 = new ArrayList<>() list2.add("Foo") list2.add("Gob") expect: list1.intersect(list2).size() == 1 }
For the purpose of testing intersection of complex objects we will create a domain class Employee. Each employee will have an id, name, role and a phone number:
@EqualsAndHashCode class Employee implements Comparable { String id String username String role String phone @Override int compareTo(Object o) { int result = workerId <=> o.workerId ?: username <=> o.username ?: role <=> o.role result } }
In order for the intersect
method to work on objects, you MUST provide a comparison method. See How to implement a Comparable interface in domain objects in Grails for more details.
We will create two lists of employees and find common employees in both lists. Here is a Groovy code that will find an intersection between to collections of employees:
def findCommon(ArrayList list1, ArrayList list2) { list1.intersect(list2) }
Here’s the groovy test code example. We created two lists where employee2 is the common object.
void "should find 1 common employee"() { setup: def employee1 = new Employee(id: '1', username: 'employee1', role: 'leader') def employee2 = new Employee(id: '2', username: 'employee2', role: 'worker') def employee3 = new Employee(id: '3', username: 'employee3', role: 'worker') ArrayList list1 = new ArrayList() list1.add(employee1) list1.add(employee2) ArrayList list2 = new ArrayList<>() list2.add(employee2) list2.add(employee3) expect: findCommon(list1, list2).size() == 1 }
Now we will take the above example a little bit further and remove the common objects from both lists. The result will be 2 lists with unique objects.
For this we will use the Groovy method removeAll
.
In order for the removeAll method to work, we need to implement Equals
and HashCode
methods in our domain objects.
Refer to the Recipe for a high quality equals method post for details on how to implement the methods.
In Groovy, however, we can also use the @EqualsAndHashCode
annotation instead of implementing the methods by hand. This will work for our simple case of an Employee object.
void "should remove 1 common employee"() { setup: def employee1 = new Employee(id: '1', username: 'employee1', role: 'leader') def employee2 = new Employee(id: '2', username: 'employee2', role: 'worker') def employee3 = new Employee(id: '3', username: 'employee3', role: 'worker') ArrayList list1 = new ArrayList() list1.add(employee1) list1.add(employee2) ArrayList list2 = new ArrayList<>() list2.add(employee2) list2.add(employee3) def commons = findCommon(list1, list2) list1.removeAll(commons) list2.removeAll(commons) expect: list1.size() == 1 list2.size() == 1 }