简化java流以查找重复的属性

nim*_*o23 3 java java-stream

我有一个users列表,我想找到所有重名的用户:

var allNames = users
              .stream()
              .map(u -> u.getName()).collect(Collectors.toList());

var duplicateNames = allNames
                .stream()
                .filter(i -> Collections.frequency(allNames, i) > 1)
                .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

我可以改进/简化上述解决方案吗?

例如,实际上我创建了一个包含所有名称的列表,然后对其进行过滤。如何遍历列表以查找其重复名称而不创建附加列表allNames

Hol*_*ger 6

一种解决方案是

var duplicate = users.stream()
    .collect(Collectors.toMap(User::getName, u -> false, (x,y) -> true))
    .entrySet().stream()
    .filter(Map.Entry::getValue)
    .map(Map.Entry::getKey)
    .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

这会创建一个中间体Map<String,Boolean>来记录哪个名称出现不止一次。您可以使用该keySet()地图的 ,而不是收集到新的Set

var duplicate = users.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(User::getName, u -> false, (x,y) -> true, HashMap::new),
            m -> {
                m.values().removeIf(dup -> !dup);
                return m.keySet();
            }));
Run Code Online (Sandbox Code Playgroud)

循环解决方案可以简单得多:

HashSet<String> seen = new HashSet<>(), duplicate = new HashSet<>();
for(User u: users)
    if(!seen.add(u.getName())) duplicate.add(u.getName());
Run Code Online (Sandbox Code Playgroud)

  • @RavindraRanwala 从技术上讲,流解决方案也只传递源集合一次。第二次迭代将在映射上进行,该映射可能已经包含比原始集合更少的元素,因为它只包含唯一键。但是,是的,这仍然比单个循环有更多的开销。 (2认同)