Java 8:比较不同类型列表的更有效方法?
Java 8: More efficient way of comparing lists of different types?
在单元测试中,我想验证两个列表是否包含相同的元素。要测试的列表是 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
class被定义为:
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>
或其他.
如果元素个数必须相同,那么比较集合比较好:
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
更方便,因为在失败的情况下,错误消息将包含您的集合的字符串表示形式。
您问题的代码未反映您在评论中描述的内容。在评论中你说所有的名字都应该存在并且大小应该匹配,换句话说,只是顺序可能不同。
您的密码是
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
替换您的静态集合,第一个解决方案不需要临时集合,而后者没有优势。
这就是我解决 2 个 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");}
希望对有需要的人有所帮助
在单元测试中,我想验证两个列表是否包含相同的元素。要测试的列表是 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
class被定义为:
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>
或其他.
如果元素个数必须相同,那么比较集合比较好:
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
更方便,因为在失败的情况下,错误消息将包含您的集合的字符串表示形式。
您问题的代码未反映您在评论中描述的内容。在评论中你说所有的名字都应该存在并且大小应该匹配,换句话说,只是顺序可能不同。
您的密码是
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
替换您的静态集合,第一个解决方案不需要临时集合,而后者没有优势。
这就是我解决 2 个 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");}
希望对有需要的人有所帮助