限制流并查明是否有待处理的元素

时间:2016-03-16 16:26:59

标签: java loops iterator java-8 java-stream

我有以下代码要转换为Java 8流:

public ReleaseResult releaseReources() {
    List<String> releasedNames = new ArrayList<>();

    Stream<SomeResource> stream = this.someResources();

    Iterator<SomeResource> it = stream.iterator();
    while (it.hasNext() && releasedNames.size() < MAX_TO_RELEASE) {
        SomeResource resource = it.next();
        if (!resource.isTaken()) {
            resource.release();
            releasedNames.add(resource.getName());
        }
    }

    return new ReleaseResult(releasedNames, it.hasNext(), MAX_TO_RELEASE);
}

方法someResources()返回Stream<SomeResource>ReleaseResult类如下:

public class ReleaseResult {

    private int releasedCount;

    private List<String> releasedNames;

    private boolean hasMoreItems;

    private int releaseLimit;

    public ReleaseResult(List<String> releasedNames, 
        boolean hasMoreItems, int releaseLimit) {

        this.releasedNames = releasedNames;
        this.releasedCount = releasedNames.size();
        this.hasMoreItems = hasMoreItems;
        this.releaseLimit = releaseLimit;
    }

    // getters & setters
}

到目前为止我的尝试:

public ReleaseResult releaseReources() {

    List<String> releasedNames = this.someResources()
        .filter(resource -> !resource.isTaken())
        .limit(MAX_TO_RELEASE)
        .peek(SomeResource::release)
        .map(SomeResource::getName)
        .collect(Collectors.toList());

    return new ReleasedResult(releasedNames, ???, MAX_TO_RELEASE);
}

问题是我找不到办法知道是否有待处理的待处理资源。我曾考虑使用releasedNames.size() == MAX_TO_RELEASE,但这并未考虑资源流具有完全 MAX_TO_RELEASE元素的情况。

有没有办法对Java 8流做同样的事情?

注意:我不是在寻找像这样的答案“你不需要用流做任何事情”“使用循环并且迭代器很好“。我很好,如果使用迭代器和循环是唯一的方法或只是最好的方式。只是我想知道是否有一种非模糊的方式来做同样的事情。

3 个答案:

答案 0 :(得分:2)

既然你不想听到你不需要所有东西和循环以及迭代器的流,那么让我们通过展示一个干净的解决方案来展示它,而不是依赖于peek

public ReleaseResult releaseReources() {
    return this.someResources()
        .filter(resource -> !resource.isTaken())
        .limit(MAX_TO_RELEASE+1)
        .collect(
            () -> new ReleaseResult(new ArrayList<>(), false, MAX_TO_RELEASE),
            (result, resource) -> {
                List<String> names = result.getReleasedNames();
                if(names.size() == MAX_TO_RELEASE) result.setHasMoreItems(true);
                else {
                    resource.release();
                    names.add(resource.getName());
                }
            },
            (r1, r2) -> {
                List<String> names = r1.getReleasedNames();
                names.addAll(r2.getReleasedNames());
                if(names.size() > MAX_TO_RELEASE) {
                    r1.setHasMoreItems(true);
                    names.remove(MAX_TO_RELEASE);
                }
            }
        );
}

这假设// getters & setters包含final的所有非ReleaseResult字段的getter和setter。并且getReleasedNames()通过引用返回列表。否则,您必须重写它以提供对Collector具有特殊非公共访问权限的专用ReleaseResult(实现另一个构建器类型或临时存储将是不必要的并发症,它看起来像ReleaseResult已经完全针对该用例设计了。)

我们可以得出结论,对于任何不适合流的内部操作的非平凡循环代码,您可以找到一个基本上与其accumulator函数中的循环相同的收集器解决方案,但是受到影响总是必须提供combiner功能的要求。好吧,在这种情况下,我们可以添加一个filter(…).limit(…),所以它不是那么糟糕......

我刚注意到,如果您敢于使用并行流,则需要一种方法来反转释放组合器中最后一个元素的效果,以防组合大小超过MAX_TO_RELEASE。通常,限制和并行处理永远不会很好。

答案 1 :(得分:1)

我唯一能想到的是

List<SomeResource> list = someResources();  // A List, rather than a Stream, is required
List<Integer> indices = IntStream.range(0, list.size())
                                 .filter(i -> !list.get(i).isTaken())
                                 .limit(MAX_TO_RELEASE)
                                 .collect(Collectors.toList());
List<String> names = indices.stream()
                            .map(list::get)
                            .peek(SomeResource::release)
                            .map(SomeResource::getName)
                            .collect(Collectors.toList());

然后(我认为)如果

,则有未处理的元素
names.size() == MAX_TO_RELEASE 
    && (indices.isEmpty() || indices.get(indices.size() - 1) < list.size() - 1)

答案 2 :(得分:1)

我认为这不是一个很好的方法。我发现了一个懒散的黑客行为。您可以做的是将Stream转换为Iterator,将Iterator转换回另一个Stream,执行Stream操作,然后最终测试{ {1}}代表下一个元素!

Iterator