Java 8:比较不同类型列表的更有效方法?

Seb*_* S. 12 comparison lambda java-8 java-stream

在单元测试中,我想验证两个列表包含相同的元素.要测试的列表是Person对象列表的构建,其中String提取了一个类型的字段.另一个列表包含String文字.

人们经常会找到以下代码片段来完成此任务(请参阅此答案):

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

assertTrue(people.stream().map(person -> person.getName()).collect(Collectors.toList()).containsAll(expectedValues));
Run Code Online (Sandbox Code Playgroud)

Person课程如下:

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
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,使用Java 8技术将人员(或人员)列表转换为字符串列表,并且以旧式方式进行比较.

现在我想知道,如果有更直接或更有效的方法使用其他Java 8语句进行比较,例如allMatch()或某些Predicate<T>或其他东西.

Hol*_*ger 20

您的问题代码并未反映您在评论中描述的内容.在评论中,您说所有名称都应该存在且大小应该匹配,换句话说,只有订单可能不同.

你的代码是

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

assertTrue(people.stream().map(person -> person.getName())
                 .collect(Collectors.toList()).containsAll(expectedValues));
Run Code Online (Sandbox Code Playgroud)

缺乏对大小的测试people,换句话说允许重复.此外,使用containsAll组合两个Lists的效率非常低.如果您使用反映您意图的集合类型,即没有重复,不关心订单并且具有高效查找,则会好得多:

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

使用此解决方案,您无需手动测试大小,已经暗示如果它们匹配,则集合具有相同的大小,只有顺序可能不同.

有一个解决方案,不需要收集以下名称persons:

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

但它只有在expectedNames是由预期名称的静态集合创建的临时集时才有效.一旦您决定用a替换静态集合Set,第一个解决方案不需要临时设置,后者没有优势.