流已经被操作或关闭

stream has already been operated upon or closed

方法是

private List<HighWay> getWillSave(List<HighWay> total,
        HighWayRepository repository) {

    List<HighWay> willSave = new ArrayList<HighWay>();
    int lastSaved = total.size() - 1;
    while (lastSaved >= 0
            && repository.exists(total.get(lastSaved).getId())) {
        lastSaved--;
    }
    willSave.addAll(total.subList(0, ++lastSaved));
    return willSave;
}

现在想改成这样,不知道改对不对:

private List<HighWay> getWillSave(Stream<HighWay> notDealItem,
        HighWayRepository repository) {

    List<HighWay> willSave = new ArrayList<HighWay>();
    long lastSaved = notDealItem.count()-1;
    while (lastSaved >= 0
            && repository.exists(notDealItem.skip(lastSaved-1).findFirst().get().getId())) {
        lastSaved--;
    }
    willSave.addAll(notDealItem.collect(Collectors.toList()).subList(0, (int)(++lastSaved)));
    return willSave;
}

但是抛出异常:

java.lang.IllegalStateException: stream has already been operated upon or closed

我知道流只能消费一次。而count和get方法都是consumer方式。但我真的很想知道计数并调用get方法。

如何避免异常和解决问题?

我应该以某种方式重构代码吗?

好吧,我想你可以做类似的事情......但与我们用 真正的 函数式语言可以做的相比,它非常不优雅:

private List<HighWay> getWillSave(List<HighWay> total, HighWayRepository repository) {
    List<HighWay> willSave = new ArrayList<HighWay>();
    int size = total.size();
    int index = 
        IntStream.range(0,size)
            .mapToObj(i -> new Pair<>(i,size - i - 1))
            .filter(p -> !repository.exists(p.getValue().getId()))
            .findFirst()
            .orElseGet(() -> new Pair<>(0,null)).getKey();
    return total.subList(0,index);
}

如果我理解您的代码逻辑,您想要 return list/stream 中的所有项目,直到您处于剩余未通过测试的点 repository.exists。那是对的吗?如果是这样,那么可以使用自定义收集器来实现:

class MyCollector {
    private List<Highway> acceptedList = new ArrayList<>();
    private List<Highway> currentList = new ArrayList<>();
    public void accept(Highway highway) {
        if (repostitory.exists(highway.getId())) {
            acceptedList.addAll(currentList);
            acceptedList.add(highway);
            currentList.clear();
        } else {
            currentList.add(highway);
        }
    }
    public Stream<Highway> stream() {
        return acceptedList.stream();
    }
}

我希望清楚它是如何工作的:如果项目未通过测试,它们将被放入临时列表,直到找到通过的项目。对于最后一系列失败的项目,它们永远不会放回最终列表中。为了简单起见,我跳过了组合器。

然后可以将其应用于您的高速公路流,如下所示:

highwayStream()
    .collect(MyCollector::new, MyCollector::accept, null)
    .stream()

然后你可以做任何你想做的事,包括 .collect(Collectors.toList()) 如果你真的需要 return 一个 List 而不是另一个流。

在提供了这个解决方案之后,我要说的是,我不相信流应该用于所有目的。它们最适合对项目进行相当独立操作的情况。在你的情况下,最终列表非常依赖于其他项目。

更直接的解决方案可能是:

List<Highway> filteredList = new ArrayList(originalList);
int i = filteredList.size() - 1;
while (i >= 0 && repository.exists(filteredList.get(i).getId()))
    filteredList.remove(i--);
return filteredList;