用迭代器和收集整理流?

dhb*_*lah 4 java java-8 java-stream

说,我想过滤列表并返回过滤后的列表,但迭代器也足够了.以下哪个选项更可取?为什么?Stream.iterator()Stream.collect(ListCollector).

Hol*_*ger 15

Stream.iterator()和之间存在根本区别.collect(Collectors.toList()) .iterator().后者将处理流的所有项目,以便将它们存储到集合中.与此相反,Stream.iterator()将刚刚返回各地的包装StreamSpliterator将处理所有项目懒洋洋地像所有其他的流操作做.

比如你写的时候

Iterator<String> it=IntStream.range(0, 100).mapToObj(i->{
    System.out.println("processing "+i);
    return String.valueOf(i);
}).iterator();

if(it.hasNext()) System.out.println("first: "+it.next());
if(it.hasNext()) System.out.println("second: "+it.next());
return;// I don’t care about the remaining values
Run Code Online (Sandbox Code Playgroud)

它将打印:

processing 0
first: 0
processing 1
second: 1
Run Code Online (Sandbox Code Playgroud)

Iterator<String> it=IntStream.range(0, 100).mapToObj(i->{
    System.out.println("processing "+i);
    return String.valueOf(i);
}).collect(Collectors.toList()).iterator();

if(it.hasNext()) System.out.println("first: "+it.next());
if(it.hasNext()) System.out.println("second: "+it.next());
return;// I don’t care about the remaining values
Run Code Online (Sandbox Code Playgroud)

将打印

processing 0
processing 1
processing 2
processing 3
processing 4
processing 5
processing 6
processing 7
processing 8
processing 9
processing 10
…
processing 90
processing 91
processing 92
processing 93
processing 94
processing 95
processing 96
processing 97
processing 98
processing 99
first: 0
second: 1
Run Code Online (Sandbox Code Playgroud)

也就是说,如果你需要的是一个Iterator你应该强制要求它之前收集的值,除非你有充分的理由这样做(例如,如果源是一个文件,你可能要返回迭代器之前完成操作).


Stu*_*rks 5

您没有提供它作为您的替代方案之一,但我建议您考虑将 Stream 返回给调用者。如果您愿意将 Iterator 返回给调用者,则 Stream 可能更方便。(Jean Logeart在评论中也提出了这一点。)

听起来您有一个内部项目(或其他)集合,并且您正在使用流根据您不想向调用者公开的某些条件来过滤它们。给定过滤后的流,返回的内容有多种选择:

  1. 一个列表: stream.collect(toList())
  2. 来自流的迭代器: stream.iterator()
  3. 来自收集列表的迭代器: stream.collect(toList()).iterator()
  4. 流本身: stream

哪个最好取决于调用者想要对返回值做什么。如果你知道调用者总是想要存储所有过滤的项目,你不妨自己收集这样一个列表来帮助调用者。

但是,调用者可能想做一些不同的事情。假设调用者想要查找特定项目,或者计算过滤项目的数量,或者查看是否有任何过滤项目。在这些情况下,将项目收集到列表中通常是一种浪费。

从过滤后的流中返回一个迭代器会让你变得懒惰,因为集合不是预先创建的。然而,处理迭代器对于调用者来说可能很麻烦。

如果调用者想要一个集合,这样的事情是必要的:

Iterator<Item> iter = getFilteredItems();
List<Item> result = new ArrayList<>();
iter.forEachRemaining(result::add);
Run Code Online (Sandbox Code Playgroud)

如果调用者想要查找特定项目,则情况更糟:

Item foundItem = null;
while (iter.hasNext()) {
    Item current = iter.next();
    if (targetId.equals(current.getId())) {
        foundItem = current;
        break;
    }
}
if (foundItem != null) {
    // found it!
} else {
    // not found
}
Run Code Online (Sandbox Code Playgroud)

计数过滤项的数量既可以做递增的计数器while (iter.hasNext())循环或通过增加一个AtomicIntegerforEachRemaining。幸运的是,测试是否有任何过滤项非常简单,只需调用iter.hasNext().

在收集的列表上返回迭代器是两全其美的。即使调用者不需要它,您也需要支付收集 List 的前期成本,而且调用者可能需要做额外的工作来遍历所有元素,如上所示。霍尔格很好地解释了这些差异。

最后,返回 Stream 提供了懒惰的效率,并且可能是最灵活的。如果您已经在使用流进行过滤,只需返回流:

Stream<Item> getFilteredItems() {
    return myInternalCollection.stream()
                               .filter(...);
}
Run Code Online (Sandbox Code Playgroud)

如果调用者想要一个项目列表,这很简单:

List<Item> = getFilteredItems().collect(toList());
Run Code Online (Sandbox Code Playgroud)

如果调用者想要查找特定项目,也很简单:

Optional<Item> item = getFilteredItems()
                        .filter(it -> targetId.equals(it.getId()))
                        .findAny();
if (item.isPresent()) {
    // found it!
} else {
    // not found
}
Run Code Online (Sandbox Code Playgroud)

Optional该类本身具有一组丰富的 API,可以让您避免ifPresent()测试。)

计数项目只是:

long count = getFilteredItems().count();
Run Code Online (Sandbox Code Playgroud)

并测试是否有任何过滤的项目是:

boolean any = getFilteredItems().findAny().isPresent();
Run Code Online (Sandbox Code Playgroud)

返回流使事情尽可能地保持懒惰,并为调用者提供最大的灵活性。