分区有限制

Fee*_*ard 5 java collections java-8 java-stream

我需要通过带有限制元素的谓词将列表拆分为两个列表true.
例如,假设我有这样的列表:A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]我希望通过谓词o -> o % 2 == 0和限制将其拆分3.
我想到达Map<Boolean, List<Integer>>哪里:

true -> [2, 4, 6] // objects by predicate and with limit (actually, order is not important)
false -> [1, 3, 5, 7, 8, 9, 10]  // All other objects
Run Code Online (Sandbox Code Playgroud)

Java 8具有通过谓词拆分流的收集器 - Collectors.partitioningBy(...)但它不支持限制.是否可以使用java 8 streams/guava/apache执行此操作,还是应该创建自己的此函数实现?

编辑:我写了这个函数.如果您对此有任何建议,请随时告诉我.MultiValuedMap是可选的,可以替换为Map.

private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(Predicate<E> predicate, List<E> src, int limit) {
    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
    Iterator<E> iterator = src.iterator();
    while (iterator.hasNext()) {
        E next = iterator.next();
        if (limit > 0 && predicate.test(next)) {
            result.put(true, next);
            iterator.remove();
            limit--;
        }
    }
    result.putAll(false, src);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

Fed*_*ner 6

以下是基于自定义收集器的方法:

public static <E> Collector<E, ?, Map<Boolean, List<E>>> partitioningByWithLimit(
        Predicate<E> predicate,
        int limit) {

    class Acc {
        Map<Boolean, List<E>> map = new HashMap<>();

        Acc() {
            map.put(true, new ArrayList<>());
            map.put(false, new ArrayList<>());
        }

        void add(E elem) {
            int size = map.get(true).size();
            boolean key = size < limit && predicate.test(elem);
            map.get(key).add(elem);
        }

        Acc combine(Acc another) {
            another.map.get(true).forEach(this::add);
            another.map.get(false).forEach(this::add);
            return this;
        }
    }

    return Collector.of(Acc::new, Acc::add, Acc::combine, acc -> acc.map));
}
Run Code Online (Sandbox Code Playgroud)

我正在使用一个Acc包装地图的本地类,并公开逻辑以累积并将流的元素组合到一个地图中.此映射根据提供的谓词和限制进行分区.

最后,我正在收集流Collector.of.

用法:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Map<Boolean, List<Integer>> map = list.stream()
        .collect(partitioningByWithLimit(n -> n % 2 == 0, 3));
Run Code Online (Sandbox Code Playgroud)

输出是:

{false=[1, 3, 5, 7, 8, 9, 10], true=[2, 4, 6]}
Run Code Online (Sandbox Code Playgroud)

这种方法的主要优点是它还支持并行流.

  • 这不会保持组合器中的遭遇顺序.事实上,在这一点上重建订单是不可能的,因为它已经丢失了. (2认同)

Hol*_*ger 4

没有干净的 Stream 解决方案,因为该任务依赖于有状态谓词。

\n\n

所以你的循环还不错,但可以清理一下:

\n\n
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(\n                                       Predicate<E> predicate, List<E> src, int limit) {\n    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();\n    for(E next: src) {\n        boolean key = limit>0 && predicate.test(next);\n        result.put(key, next);\n        if(key) limit--;\n    }\n    return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你真的想在达到极限时获得更快一点的感觉,你可以使用

\n\n
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(\n                                       Predicate<E> predicate, List<E> src, int limit) {\n    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();\n    for(Iterator<E> iterator = src.iterator(); iterator.hasNext(); ) {\n        E next = iterator.next();\n        boolean key = predicate.test(next);\n        result.put(key, next);\n        if(key && --limit==0) iterator.forEachRemaining(result.get(false)::add);\n    }\n    return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这避免了重新检查限制,甚至避免了剩余元素的映射查找,但是,我不希望\xe2\x80\x99 期望有很大的性能差异。第一个变体要简单得多。

\n\n

另一种选择是利用更多 Java\xc2\xa08 功能

\n\n
private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(\n                                       Predicate<E> predicate, List<E> src, int limit) {\n    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();\n    result.putAll(false, src);\n    List<E> pos = result.get(true);\n    result.get(false).removeIf(e -> pos.size()<limit && predicate.test(e) && pos.add(e));\n    return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n