我有一段这样的代码:
List<Egg> eggs = hens.parallelStream().map(hen -> {
ArrayList<Egg> eggs = new ArrayList<>();
while (hen.hasEgg()) {
eggs.add(hen.getEgg());
}
return eggs;
}).flatMap(Collection::stream).collect(Collectors.toList());
但是通过这种方式,我必须为每只母鸡创建一个ArrayList,并且在母鸡100%处理之前不会收集鸡蛋。我想要这样的东西:
List<Egg> eggs = hens.parallelStream().map(hen -> {
while (hen.hasEgg()) {
yield return hen.getEgg();
}
}).collect(Collectors.toList());
但是Java没有收益率。有没有办法实现它?
答案 0 :(得分:8)
您的Hen
课程很难适应Stream API。如果您无法更改它并且没有其他有用的方法(例如Collection<Egg> getAllEggs()
或Iterator<Egg> eggIterator()
),您可以创建如下的蛋流:
public static Stream<Egg> eggs(Hen hen) {
Iterator<Egg> it = new Iterator<Egg>() {
@Override
public boolean hasNext() {
return hen.hasEgg();
}
@Override
public Egg next() {
return hen.getEgg();
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false);
}
现在您可以通过以下方式使用它:
List<Egg> eggs = hens.parallelStream()
.flatMap(hen -> eggs(hen))
.collect(Collectors.toList());
当然,如果您可以更改Stream
类,则可以实现更好的Hen
实施。
答案 1 :(得分:2)
使用hasEgg()
和getEgg()
的迭代逻辑是有状态,因为这些方法的结果取决于之前的调用。因此,除非您设法完全更改接口,否则无法并行处理单个Hen
。
那就是说,你担心ArrayList
是不必要的。当流实现并行执行collect
操作时,它必须缓冲每个线程的值,然后组合这些缓冲区。甚至可能是操作根本没有从并行执行中受益。
您可以做的是将ArrayList
替换为Stream.Builder
,因为它针对仅在构建Stream
之前添加的用例进行了优化:
List<Egg> eggs = hens.parallelStream().flatMap(hen -> {
Stream.Builder<Egg> eggStream = Stream.builder();
while(hen.hasEgg()) {
eggStream.add(hen.getEgg());
}
return eggStream.build();
}).collect(Collectors.toList());
答案 2 :(得分:1)
假设存在getEggs()
方法,您可以使用以下方法收集所有鸡蛋。
List<Egg> eggs = hens.parallelStream()
.filter(Hen::hasEggs)
.map(Hen::getEggs)
.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
代码假定getEggs()
返回Collection
。如果filter(Hen::hasEggs)
在getEggs()
没有Collection
时返回空Hen
,则可以删除Eggs
。