流已经被操作或关闭

时间:2015-02-02 02:23:12

标签: java java-8 java-stream

方法是

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已被操作   或关闭

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

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

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

2 个答案:

答案 0 :(得分:0)

嗯,我想你可以做那样的事......但与我们用真正的功能语言做的事情相比,这是非常不优雅的:

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);
}

答案 1 :(得分:0)

如果我理解您的代码的逻辑,您希望返回列表/流中的所有项目,直到您处于其余部分未通过测试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())而不是其他信息流,则可以执行任何操作,包括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;