2015-05-19 4 views
8

В модульном тесте я хочу проверить, что два списка содержат одни и те же элементы. Список для тестирования состоит из списка объектов Person, где извлекается одно поле типа String. В другом списке содержится String литералов.Java 8: Более эффективный способ сравнения списков разных типов?

Часто находит следующий фрагмент кода для выполнения этой задачи (см 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)); 

Person класс defiend как:

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 
} 

В приведенном выше примере, список лиц, (или люди) преобразуется в список строк с использованием методов Java 8, и сравнение выполняется по-старому.

Теперь интересно, если есть более прямой или более эффективный способ сравнения, используя другие операторы Java 8, например allMatch() или некоторые Predicate<T> или что-то еще.

+0

Почему бы не использовать [Hamcrest] (http://hamcrest.org/) вместо них? – Makoto

+0

Вы действительно хотите проверить 'containsAll'? Значит, порядок и размер списков могут быть разными? –

+0

@TagirValeev Я хочу, чтобы «List people» содержал всех людей, которые я указывал в статическом списке, сравнивая их уникальные имена. Таким образом, порядок списков может отличаться, но для успешного утверждения размер списков должен быть одинаковым. –

ответ

13

Код вопроса не содержит описание того, что вы описали в комментариях. В комментариях вы говорите, что все имена должны присутствовать и размер должен соответствовать, другими словами, только порядок может отличаться.

Ваш код

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

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

которой не хватает тест на размер people, другими словами позволяет дубликаты. Кроме того, использование containsAll, объединяющее два List с в очень неэффективном режиме. Это гораздо лучше, если вы используете тип коллекции, которая отражает вас намерение, то есть не имеют дубликатов, не заботятся о порядке и имеет эффективный поиск:

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

с этим решением, которое вы не должны проверить для размер вручную, уже подразумевается, что наборы имеют одинаковый размер, если они совпадают, только порядок может отличаться.

Существует решение, которое не требует сбора имен из persons:

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

, но это работает только в том случае expectedNames временный набор создан из статической коллекции ожидаемых имен. Как только вы решите заменить свою статическую коллекцию на Set, первое решение не требует временного набора, и последнее не имеет никакого преимущества перед ним.

+0

Я признаю, что у меня также есть проверка размеров списков, но я не написал его в коде вопроса. И вы правы в использовании наборов вместо списков. Ваше второе решение - это то, что я ожидал получить. Но, как вы сказали, нет никакого преимущества перед вашим первым решением, я выбираю этот. Но теперь я использую 'assertEquals' вместо' assertTrue'. –

4

Если число элементов должно быть таким же, то лучше было бы сравнить наборы:

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

Метод equals должным образом реализованы наборы должны иметь возможность сравнивать различные типы наборов: он просто проверяет, независимо от того, является ли содержимое одинаковым (игнорируя порядок, конечно).

Использование assertEquals более удобно, так как в случае сбоя сообщение об ошибке будет содержать строковое представление вашего набора.

+0

Да, использование наборов абсолютно имеет смысл. –

Смежные вопросы