Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8: More efficient way of comparing lists of different types?

In a unit test, I want to verify that two lists contain the same elements. The list to test is build of a list of Person objects, where one field of type String is extracted. The other list contains String literals.

One often finds the following code snippet to accomplish this task (see this answer):

List<Person> people = getPeopleFromDatabasePseudoMethod();
List<String> expectedValues = Arrays.asList("john", "joe", "bill");

assertTrue(people.stream().map(person -> person.getName()).collect(Collectors.toList()).containsAll(expectedValues));

The Personclass is defiend as:

public class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    // other getters and setters
}

In the example above, the list of persons (or people) is transformed to a list of Strings using Java 8 techniques and the comparision is done in the old-fashioned way.

Now I wonder, if there is a more direct or more efficient way of doing the comparison using other Java 8 statements, for example allMatch() or some Predicate<T> or something else.

like image 995
Sebastian S. Avatar asked May 19 '15 16:05

Sebastian S.


People also ask

How do I compare one list with another list in Java 8?

how to compare two lists in java using equals() and containsAll() method. These two methods take List as an argument and compare each and every object are same in each list. equals() method is overridden in ArrayList class.

How do I compare two lists in Java and get differences?

Using the Java List API. We can create a copy of one list and then remove all the elements common with the other using the List method removeAll(): List<String> differences = new ArrayList<>(listOne); differences. removeAll(listTwo); assertEquals(2, differences.

How do I compare two lists of data in Java?

Java provides a method for comparing two Array List. The ArrayList. equals() is the method used for comparing two Array List. It compares the Array lists as, both Array lists should have the same size, and all corresponding pairs of elements in the two Array lists are equal.


2 Answers

Your question’s code does not reflect what you describe in the comments. In the comments you say that all names should be present and the size should match, in other words, only the order may be different.

Your code is

List<Person> people = getPeopleFromDatabasePseudoMethod();
List<String> expectedValues = Arrays.asList("john", "joe", "bill");

assertTrue(people.stream().map(person -> person.getName())
                 .collect(Collectors.toList()).containsAll(expectedValues));

which lacks a test for the size of people, in other words allows duplicates. Further, using containsAll combining two Lists in very inefficient. It’s much better if you use a collection type which reflects you intention, i.e. has no duplicates, does not care about an order and has an efficient lookup:

Set<String> expectedNames=new HashSet<>(expectedValues);
assertTrue(people.stream().map(Person::getName)
                 .collect(Collectors.toSet()).equals(expectedNames));

with this solution you don’t need to test for the size manually, it is already implied that the sets have the same size if they match, only the order may be different.

There is a solution which does not require collecting the names of persons:

Set<String> expectedNames=new HashSet<>(expectedValues);
assertTrue(people.stream().allMatch(p->expectedNames.remove(p.getName()))
           && expectedNames.isEmpty());

but it only works if expectedNames is a temporary set created out of the static collection of expected names. As soon as you decide to replace your static collection by a Set, the first solution doesn’t require a temporary set and the latter has no advantage over it.

like image 187
Holger Avatar answered Sep 30 '22 02:09

Holger


If the number of elements must be the same, then it would be better to compare sets:

List<Person> people = getPeopleFromDatabasePseudoMethod();
Set<String> expectedValues = new HashSet<>(Arrays.asList("john", "joe", "bill"));
assertEquals(expectedValues, 
    people.stream().map(Person::getName).collect(Collectors.toSet()));

The equals method for properly implemented sets should be able to compare different types of sets: it just checks whether the contents is the same (ignoring the order of course).

Using assertEquals is more convenient as in case of failure an error message will contain the string representation of your set.

like image 22
Tagir Valeev Avatar answered Sep 30 '22 02:09

Tagir Valeev