我有以下代码要转换为Java 8流:
public ReleaseResult releaseReources() {
List<String> releasedNames = new ArrayList<>();
Stream<SomeResource> stream = this.someResources();
Iterator<SomeResource> it = stream.iterator();
while (it.hasNext() && releasedNames.size() < MAX_TO_RELEASE) {
SomeResource resource = it.next();
if (!resource.isTaken()) {
resource.release();
releasedNames.add(resource.getName());
}
}
return new ReleaseResult(releasedNames, it.hasNext(), MAX_TO_RELEASE);
}
方法someResources()
返回Stream<SomeResource>
和ReleaseResult
类如下:
public class ReleaseResult {
private int releasedCount;
private List<String> releasedNames;
private boolean hasMoreItems;
private int releaseLimit;
public ReleaseResult(List<String> releasedNames,
boolean hasMoreItems, int releaseLimit) {
this.releasedNames = releasedNames;
this.releasedCount = releasedNames.size();
this.hasMoreItems = hasMoreItems;
this.releaseLimit = releaseLimit;
}
// getters & setters
}
到目前为止我的尝试:
public ReleaseResult releaseReources() {
List<String> releasedNames = this.someResources()
.filter(resource -> !resource.isTaken())
.limit(MAX_TO_RELEASE)
.peek(SomeResource::release)
.map(SomeResource::getName)
.collect(Collectors.toList());
return new ReleasedResult(releasedNames, ???, MAX_TO_RELEASE);
}
问题是我找不到办法知道是否有待处理的待处理资源。我曾考虑使用releasedNames.size() == MAX_TO_RELEASE
,但这并未考虑资源流具有完全 MAX_TO_RELEASE
元素的情况。
有没有办法对Java 8流做同样的事情?
注意:我不是在寻找像这样的答案“你不需要用流做任何事情”或“使用循环并且迭代器很好“。我很好,如果使用迭代器和循环是唯一的方法或只是最好的方式。只是我想知道是否有一种非模糊的方式来做同样的事情。
答案 0 :(得分:2)
既然你不想听到你不需要所有东西和循环以及迭代器的流,那么让我们通过展示一个干净的解决方案来展示它,而不是依赖于peek
:
public ReleaseResult releaseReources() {
return this.someResources()
.filter(resource -> !resource.isTaken())
.limit(MAX_TO_RELEASE+1)
.collect(
() -> new ReleaseResult(new ArrayList<>(), false, MAX_TO_RELEASE),
(result, resource) -> {
List<String> names = result.getReleasedNames();
if(names.size() == MAX_TO_RELEASE) result.setHasMoreItems(true);
else {
resource.release();
names.add(resource.getName());
}
},
(r1, r2) -> {
List<String> names = r1.getReleasedNames();
names.addAll(r2.getReleasedNames());
if(names.size() > MAX_TO_RELEASE) {
r1.setHasMoreItems(true);
names.remove(MAX_TO_RELEASE);
}
}
);
}
这假设// getters & setters
包含final
的所有非ReleaseResult
字段的getter和setter。并且getReleasedNames()
通过引用返回列表。否则,您必须重写它以提供对Collector
具有特殊非公共访问权限的专用ReleaseResult
(实现另一个构建器类型或临时存储将是不必要的并发症,它看起来像ReleaseResult
已经完全针对该用例设计了。)
我们可以得出结论,对于任何不适合流的内部操作的非平凡循环代码,您可以找到一个基本上与其accumulator
函数中的循环相同的收集器解决方案,但是受到影响总是必须提供combiner
功能的要求。好吧,在这种情况下,我们可以添加一个filter(…).limit(…)
,所以它不是那么糟糕......
我刚注意到,如果您敢于使用并行流,则需要一种方法来反转释放组合器中最后一个元素的效果,以防组合大小超过MAX_TO_RELEASE
。通常,限制和并行处理永远不会很好。
答案 1 :(得分:1)
我唯一能想到的是
List<SomeResource> list = someResources(); // A List, rather than a Stream, is required
List<Integer> indices = IntStream.range(0, list.size())
.filter(i -> !list.get(i).isTaken())
.limit(MAX_TO_RELEASE)
.collect(Collectors.toList());
List<String> names = indices.stream()
.map(list::get)
.peek(SomeResource::release)
.map(SomeResource::getName)
.collect(Collectors.toList());
然后(我认为)如果
,则有未处理的元素names.size() == MAX_TO_RELEASE
&& (indices.isEmpty() || indices.get(indices.size() - 1) < list.size() - 1)
答案 2 :(得分:1)
我认为这不是一个很好的方法。我发现了一个懒散的黑客行为。您可以做的是将Stream
转换为Iterator
,将Iterator
转换回另一个Stream
,执行Stream
操作,然后最终测试{ {1}}代表下一个元素!
Iterator