我可以限制 Collectors.toMap() 个条目吗?

Can I limit Collectors.toMap() entries?

我正在寻找一种方法来限制 Collectors.toMap() 使用合并功能生成的条目数。考虑以下示例:

Map<String, Integer> m = Stream.of("a", "a", "b", "c", "d")
    .limit(3)
    .collect(toMap(Function.identity(), s -> 1, Integer::sum));

上面的问题是我在生成的地图中只有 2 个元素 (a=2, b=1)。处理完 3 distinct keys 后,有什么方便的方法可以使流短路?

一个可能的解决方案是编写您自己的 Spliterator,它将包装给定 Stream 的拆分器。此 Spliterator 会将推进调用委托给包装的拆分器,并包含对出现的许多不同元素进行计数的逻辑。

为此,我们可以子类化 AbstractSpliterator 并提供我们自己的 tryAdvance 逻辑。在下文中,遇到的所有元素都被添加到一个集合中。当该集合的大小变得大于我们的最大值或当包装的拆分器没有剩余元素时,我们 return false 表示没有剩余元素需要考虑。当达到不同元素的数量时,这将停止。

private static <T> Stream<T> distinctLimit(Stream<T> stream, int max) {
    Spliterator<T> spltr = stream.spliterator();
    Spliterator<T> res = new AbstractSpliterator<T>(spltr.estimateSize(), spltr.characteristics()) {

        private Set<T> distincts = new HashSet<>();
        private boolean stillGoing = true;

        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            boolean hasRemaining = spltr.tryAdvance(elem -> {
                distincts.add(elem);
                if (distincts.size() > max) {
                    stillGoing = false;
                } else {
                    action.accept(elem);
                }
            });
            return hasRemaining && stillGoing;
        }
    };
    return StreamSupport.stream(res, stream.isParallel()).onClose(stream::close);
}

使用您的示例代码,您将拥有:

Map<String, Long> m =
    distinctLimit(Stream.of("a", "a", "b", "c", "d"), 3)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

并且输出将是预期的 {a=2, b=1, c=1},即具有 3 个不同键的映射。