dhb*_*lah 4 java java-8 java-stream
说,我想过滤列表并返回过滤后的列表,但迭代器也足够了.以下哪个选项更可取?为什么?Stream.iterator()或Stream.collect(ListCollector).
Hol*_*ger 15
Stream.iterator()和之间存在根本区别.collect(Collectors.toList()) .iterator().后者将处理流的所有项目,以便将它们存储到集合中.与此相反,Stream.iterator()将刚刚返回各地的包装Stream的Spliterator将处理所有项目懒洋洋地像所有其他的流操作做.
比如你写的时候
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你应该不强制要求它之前收集的值,除非你有充分的理由这样做(例如,如果源是一个文件,你可能要返回迭代器之前完成操作).
您没有提供它作为您的替代方案之一,但我建议您考虑将 Stream 返回给调用者。如果您愿意将 Iterator 返回给调用者,则 Stream 可能更方便。(Jean Logeart在评论中也提出了这一点。)
听起来您有一个内部项目(或其他)集合,并且您正在使用流根据您不想向调用者公开的某些条件来过滤它们。给定过滤后的流,返回的内容有多种选择:
stream.collect(toList())stream.iterator()stream.collect(toList()).iterator()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())循环或通过增加一个AtomicInteger内forEachRemaining。幸运的是,测试是否有任何过滤项非常简单,只需调用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)
返回流使事情尽可能地保持懒惰,并为调用者提供最大的灵活性。