I have a stream of objects similar to this previous question,但是,我不想忽略重复的值,而是希望事先从该流中删除任何值并将其打印出来.

例如,从这个片段:

Map<String, String> phoneBook = people.stream()
                                      .collect(toMap(Person::getName,
                                                     Person::getAddress));

如果存在重复条目,则会引发java.lang.IllegalStateException: Duplicate key错误.

该问题中提出的解决方案是,如果发现碰撞,使用mergeFunction保留第一个条目.

Map<String, String> phoneBook = 
    people.stream()
          .collect(Collectors.toMap(
             Person::getName,
             Person::getAddress,
             (address1, address2) -> {
                 System.out.println("duplicate key found!");
                 return address1;
             }
          ));

如果流中存在来自重复键的冲突,我不想保留第一个条目,而是想知道是哪个值导致了冲突,并确保在生成的映射中没有出现该值.

也就是说,如果"Bob"在流中出现了三次,那么它不应该出现在map中,哪怕是一次.

在创建 map 的过程中,我想过滤掉任何重复的名字,并以某种方式记录下来.

我想确保在创建列表时,不能有重复的条目,并且可以通过某种方式知道哪些条目在传入流中有重复的密钥.我之前考虑过使用groupingByfilter来找到重复的 keys ,但我不确定最好的方法是什么.

推荐答案

我希望事先从该流中删除任何值.

正如@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中的唯一值和重复值,这将包含电话簿的duplicatedunique个条目的单独集合.

由于这个对象的主要功能只是携带数据,所以我将其实现为Java 16条记录.

public record FilteredPhoneBook(Map<String, String> uniquePersonsAddressByName,
                                List<String> duplicatedNames) {}

收集器teeing()需要三个参数:两个collectors和一个function,用于合并两个收集器生成的结果.

groupingBy()counting()一起生成的map用于确定重复的名称.

因为没有必要处理数据,所以用作second collectortoMap()将创建一个包含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

Java相关问答推荐

将linkedHashMap扩展到Java中的POJO类

当一个链表中间有一个循环时,它的松散部分会发生什么?

当我用OkHttpClient重写shouldInterceptRequest来发布数据时,Android WebView正在以纯HTML加载URL内容

为什么我们仍然需要实现noArgsConstructor如果Java默认提供一个非参数化的构造函数?''

Select 按位运算序列

neo4j java驱动程序是否会在错误发生时自动回滚事务?

使用动态ID从json获取详细信息的Jolt规范

上下文初始化期间遇到异常-使用Java配置配置HibernateTemplate Bean时

是否保证在事务性块的末尾标记违反约束?

如何修复IndexOutOfBoundsException在ClerView适配器的onRowMoved函数?

由于我在Main方法中关闭了 scanner ,但在该方法中创建了一个新的 scanner ,因此出现了错误

如何读取3个CSV文件并在控制台中按顺序显示?(Java)

如何获得凌空cookies ,并设置它在下一个请求- android

使用迭代器遍历HashMap不会因IF条件而停止

处理4.3问题:javax.xml.ind包不存在(&Q;).您可能在学习GitHub教程时遗漏了库.&Q

在权限列表中找不到我的应用程序

HBox内部的左对齐按钮(如果重要的话,在页码内)

将@Transactional添加到Spring框架中链下的每个方法会产生什么效果?

为什么我得到默认方法的值而不是被覆盖的方法的值?

转换为JSON字符串时,日期按天递减-Java