我希望事先从该流中删除任何值.
正如@JimGarrison所指出的,对数据进行预处理毫无意义.
在处理完所有数据集之前,您无法预先知道名称是否唯一.
您必须考虑的另一件事是,在流管道(before the collector)中,您知道以前遇到过哪些数据.因为中间操作的结果不应该依赖于任何状态.
如果您认为流的行为就像一系列循环,因此假设可以在收集流元素之前对其进行预处理,那么这是不正确的.流管道的元素一次一个地被懒洋洋地处理.也就是说,管道中的all the operations将应用于single element,并且每个操作只有在其为needed时才会应用(这就是laziness的意思).
For more information, have a look at 100 and 101
启动位置
您可以使用Collectors.teeing()
和custom object来分离single stream statement中的唯一值和重复值,这将包含电话簿的duplicated和unique个条目的单独集合.
由于这个对象的主要功能只是携带数据,所以我将其实现为Java 16条记录.
public record FilteredPhoneBook(Map<String, String> uniquePersonsAddressByName,
List<String> duplicatedNames) {}
收集器teeing()
需要三个参数:两个collectors和一个function,用于合并两个收集器生成的结果.
groupingBy()
与counting()
一起生成的map用于确定重复的名称.
因为没有必要处理数据,所以用作second collector的toMap()
将创建一个包含all names的 map .
当两个采集器将其结果分发给merger
函数时,它将负责删除重复项.
public static FilteredPhoneBook getFilteredPhoneBook(Collection<Person> people) {
return people.stream()
.collect(Collectors.teeing(
Collectors.groupingBy(Person::getName, Collectors.counting()), // intermediate Map<String, Long>
Collectors.toMap( // intermediate Map<String, String>
Person::getName,
Person::getAddress,
(left, right) -> left),
(Map<String, Long> countByName, Map<String, String> addressByName) -> {
countByName.values().removeIf(count -> count == 1); // removing unique names
addressByName.keySet().removeAll(countByName.keySet()); // removing all duplicates
return new FilteredPhoneBook(addressByName, new ArrayList<>(countByName.keySet()));
}
));
}
另一种解决这个问题的方法是利用Map<String,Boolean>
作为发现重复的平均值,正如@Holger所建议的那样.
第一个收集器将使用toMap()
编写.它将true
与一个只遇到过一次的密钥相关联,如果发现至少一个副本,它的mergeFunction
将分配false
的值.
其余的逻辑保持不变.
public static FilteredPhoneBook getFilteredPhoneBook(Collection<Person> people) {
return people.stream()
.collect(Collectors.teeing(
Collectors.toMap( // intermediate Map<String, Boolean>
Person::getName,
person -> true, // not proved to be a duplicate and initially considered unique
(left, right) -> false), // is a duplicate
Collectors.toMap( // intermediate Map<String, String>
Person::getName,
Person::getAddress,
(left, right) -> left),
(Map<String, Boolean> isUniqueByName, Map<String, String> addressByName) -> {
isUniqueByName.values().removeIf(Boolean::booleanValue); // removing unique names
addressByName.keySet().removeAll(isUniqueByName.keySet()); // removing all duplicates
return new FilteredPhoneBook(addressByName, new ArrayList<>(isUniqueByName.keySet()));
}
));
}
main()
-演示
public static void main(String[] args) {
List<Person> people = List.of(
new Person("Alise", "address1"),
new Person("Bob", "address2"),
new Person("Bob", "address3"),
new Person("Carol", "address4"),
new Person("Bob", "address5")
);
FilteredPhoneBook filteredPhoneBook = getFilteredPhoneBook(people);
System.out.println("Unique entries:");
filteredPhoneBook.uniquePersonsAddressByName.forEach((k, v) -> System.out.println(k + " : " + v));
System.out.println("\nDuplicates:");
filteredPhoneBook.duplicatedNames().forEach(System.out::println);
}
Output
Unique entries:
Alise : address1
Carol : address4
Duplicates:
Bob