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

问题描述 投票:12回答:3

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

通常会找到以下代码片段来完成此任务(请参见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类的定义为:

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>或其他名称)进行比较的更直接或更有效的方法。

lambda comparison java-8 java-stream
3个回答
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));

缺少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,第一个解决方案就不需要临时集,而后者则没有任何优势。


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
投票

这是我如何解决两个Person列表的相等性的方法>

public class Person {
  private String name;
  //constructor, setters & getters 
  @Override
  public boolean equals(Object o) {
    if(this == o) return true;
    if(o == null || getClass() != o.getClass()) return false
    Person p = (Person) o;
    return name.equals(p.name);
  }
  @Override
  public int hashCode() { return Objects.hash(name);}
}

// call this boolean method where (in a service for instance) you want to test the equality of 2 lists of Persons.
public boolean isListsEqual(List<Person> givenList, List<Person> dbList) {
  if(givenList == null && dbList == null) return true;
  if(!givenList.containsAll(dbList) || givenList.size() != dbList.size()) return false;
  return true;
}

// You can test it in a main method or where needed after initializing 2 lists    
boolean equalLists = this.isListsEqual(givenList, dbList);
if(equalLists) { System.out.println("Equal"); }
else {System.out.println("Not Equal");}   

我希望这可以帮助需要帮助的人。

© www.soinside.com 2019 - 2024. All rights reserved.