使用 Java 8 流从重复整数列表中获取唯一编号



我正在尝试从整数列表中获取仅重复的数字列表:

final Set<Integer> setOfNmums = new HashSet<>();
Arrays.asList(5,6,7,7,7,6,2,4,2,4).stream()
.peek(integer -> System.out.println("XX -> " + integer))
.filter(n -> !setOfNmums.add(n))
.peek(System.out::println)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());
The output is 2,4,6,7,7 
Expected : 2,4,6,7

我不明白这是怎么回事.. 这是并行运行的吗? 我怎么得到两个"7"?

哈希集如果存在并且过滤器使用,则应返回 false?

是的,我可以使用不同的,但我很好奇为什么过滤器会失败......它是并行完成的吗?

筛选器拒绝每个元素的第一次出现,并接受所有后续出现。因此,当元素出现 n 次时,您将添加它n-1次。

由于您希望接受多次出现的所有元素,但只接受一次,因此您可以使用.filter(n -> !setOfNmums.add(n)) .distinct()或将集合增强为映射,以便仅在元素第二次出现时接受该元素。

Map<Integer, Integer> occurrences = new HashMap<>();
List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.filter(n -> occurrences.merge(n, 1, Integer::sum) == 2)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());

但通常,不鼓励对流使用有状态筛选器。

一个更清洁的解决方案是

List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.collect(Collectors.collectingAndThen(
Collectors.toMap(String::valueOf, x -> true, (a,b) -> false, TreeMap::new),
map -> { map.values().removeIf(b -> b); return new ArrayList<>(map.keySet()); }));

请注意,此方法不计算出现次数,而只记住元素是否唯一或至少见过第二次。这的工作原理是将每个元素映射到 true,第二个参数到toMap收集器,x -> true,并使用(a,b) -> false的合并函数解决多次出现。后续map.values().removeIf(b -> b)将删除所有唯一元素,即映射到true的元素。

您可以在流中使用 .distinct() 函数,请查看此内容。

由于 Holger 已经解释了为什么您的解决方案不起作用,我将提供一个替代方案。

为什么不Collections.frequency(collection, element)distinct()一起使用?

解决方案将非常简单(对于格式化,我深表歉意,我只是从我的 ide 中复制了它,SOF 中似乎没有自动格式化功能):

List<Integer> numbers = List.of(5, 6, 7, 7, 7, 6, 2, 4, 2, 4);
List<String> onlyDuplicates = numbers.stream()
.filter(n -> Collections.frequency(numbers, n) > 1)
.distinct()
.sorted()
.map(String::valueOf)
.toList();

这只会保留多次出现的所有元素,然后在排序之前过滤掉重复项,将每个元素转换为字符串并收集到列表,因为这似乎是您想要的。

如果您需要可变列表,您可以使用collect(toCollection(ArrayList::new))而不是toList()

最新更新