需要额外过滤时重用流 - Java

时间:2017-11-28 20:02:07

标签: java java-8 java-stream audit

总之,以下代码用于过滤第二个参数中实体的第一个参数。如果一个'改变''在第二个参数中指定,它应该过滤较窄的结果。

当我运行这个时,我得到一个错误:#IllegalStateException:stream已经被操作或关闭。'

有没有办法可以重复使用相同的流?
我见过人们实现像Supplier>这样的东西,但我认为这不适用于这种情况。或者,我对它的理解并不熟悉,以了解如何使用供应商。

/**
     * Filters through DocumentAuditEntityListing to find existence of the entities
     * ActionEnum, ActionContextEnum, LevelEnum, & StatusEnum.
     *
     * @param audits A list of audits to search
     * @param toFind The audit entities to find
     * @return If entities found, return DocumentAudit, else null
     */
    public DocumentAudit verifyAudit(DocumentAuditEntityListing audits, DocumentAudit toFind) {
        //Filter for proper entities
        Stream stream = audits.getEntities().stream().filter(doc -> (
                doc.getAction().equals(toFind.getAction())) &&
                doc.getActionContext().equals(toFind.getActionContext()) &&
                doc.getLevel().equals(toFind.getLevel()) &&
                doc.getStatus().equals(toFind.getStatus()));

        //If changes were specified, filter further.
        if (toFind.getChanges() != null){
            stream.filter(change -> (toFind.getChanges().contains(change)));
        }

        return (DocumentAudit) stream.findFirst().orElse(null);
    }

3 个答案:

答案 0 :(得分:7)

您需要使用stream = stream.filter(change -> ...)分配结果流。

还使流类型安全(即Stream<DocumentAudit>)。自Java 5以来,泛型一直存在,所以你没有理由使用原始类型。

        Stream<DocumentAudio> stream = audits.getEntities().stream()
            .filter(doc -> 
                doc.getAction().equals(toFind.getAction())) &&
                doc.getActionContext().equals(toFind.getActionContext()) &&
                doc.getLevel().equals(toFind.getLevel()) &&
                doc.getStatus().equals(toFind.getStatus());


        if (toFind.getChanges() != null){
            stream = stream.filter(change -> toFind.getChanges().contains(change));
        }

        return stream.findFirst().orElse(null);

答案 1 :(得分:2)

Supplier不是技巧,而只是用于促进新流的创建。规则仍然相同 - 在任何情况下都不能重用流。使用Supplier的一般方法是这样的:

 Supplier<Stream<Something>>  sup = () -> yourList.stream();

因此,每次拨打sup.get()时,您都会收到新的Stream。这没什么好看的,只是风格问题。

实际上你的问题与我已经看过的问题有点不同。基本上你在做:

Stream<Some> s = ...

s.filter
s.filter
s.forEach

我发现错误消息在这里有点误导,stream has already been operated upon or closed实际上意味着终端中间操作只能应用一次一个Stream。如果在错误消息中更清楚,那就更有意义了。

答案 2 :(得分:1)

错误消息令人困惑,因为没有重用流(只调用了一个终端操作,即findFirst())。

但是,正如其他人所指出的,stream.filter(...)块中if的应用程序正在丢失,因为filter操作返回的流永远不会被赋予变量。这是造成这种混淆错误的原因。

尽管如此,我还想向您展示另一种处理此要求的方法。您可以先处理谓词,然后在准备好之后将其传递给stream.filter(...)操作,而不是有条件地应用额外的过滤器:

Predicate<DocumentAudit> pred = doc ->
    doc.getAction().equals(toFind.getAction()) &&
    doc.getActionContext().equals(toFind.getActionContext()) &&
    doc.getLevel().equals(toFind.getLevel()) &&
    doc.getStatus().equals(toFind.getStatus());

// If changes were specified, filter further.
if (toFind.getChanges() != null) {
    pred = pred.and(change -> toFind.getChanges().contains(change));
}

return audits.getEntities().stream()
    .filter(pred)
    .findFirst()
    .orElse(null);

一些评论......

我用Predicate.and来组成条件。

你在最后一行使用原始流和强制转换。请不要再这样做了。我们在2017年......

理想情况下,您应该使用Optional作为方法的返回值(而不是返回找到的对象或null)。这样,您的方法的用户可以提前知道可能已找到该对象。他们甚至可以使用Optional方便的操作来有条件地对对象执行操作,或有条件地将其映射到其某些属性等。