66 java lambda java-8 java-stream
我经常遇到同样的问题.我需要计算一个lambda的运行,以便在lambda之外使用.例如:
myStream.stream().filter(...).forEach(item->{ ... ; runCount++);
System.out.println("The lambda ran "+runCount+"times");
Run Code Online (Sandbox Code Playgroud)
问题是runCount需要是final,所以它不能是int.它不能是一个整数,因为那是不可变的.我可以使它成为类级变量(即一个字段),但我只需要在这个代码块中.我知道有各种各样的方式,我只是好奇你最喜欢的解决方案是什么?您使用AtomicInteger或数组引用还是其他方式?
Stu*_*rks 64
为了讨论,让我重新格式化你的例子:
long runCount = 0L;
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
runCount++; // doesn't work
});
System.out.println("The lambda ran " + runCount + " times");
Run Code Online (Sandbox Code Playgroud)
如果你真的需要从lambda中增加一个计数器,那么这样做的典型方法是使计数器为a AtomicInteger
或AtomicLong
然后调用其中的一个增量方法.
您可以使用单个元素int
或long
数组,但如果流并行运行,则会出现竞争条件.
但请注意,流结束forEach
,这意味着没有返回值.您可以将项目更改forEach
为a peek
,然后计算它们:
long runCount = myStream.stream()
.filter(...)
.peek(item -> {
foo();
bar();
})
.count();
System.out.println("The lambda ran " + runCount + " times");
Run Code Online (Sandbox Code Playgroud)
这有点好,但仍然有点奇怪.其原因是,forEach
与peek
只能通过副作用做好自己的工作.Java 8的新兴功能风格是避免副作用.我们通过将计数器的增量提取到count
流上的操作中来做了一点.其他典型的副作用是将项目添加到集合中.通常这些可以通过使用收集器来替换.但是,如果不知道你想要做什么实际工作,我不能提出更具体的建议.
小智 27
作为同步攻击AtomicInteger的替代方法,可以使用整数数组.只要对数组的引用没有分配另一个数组(并且就是这一点),它就可以用作最终变量,而字段的值可以任意改变.
int[] iarr = {0}; // final not neccessary here if no other array is assigned
stringList.forEach(item -> {
iarr[0]++;
// iarr = {1}; Error if iarr gets other array assigned
});
Run Code Online (Sandbox Code Playgroud)
小智 12
AtomicInteger runCount = 0L;
long runCount = myStream.stream()
.filter(...)
.peek(item -> {
foo();
bar();
runCount.incrementAndGet();
});
System.out.println("The lambda ran " + runCount.incrementAndGet() + "times");
Run Code Online (Sandbox Code Playgroud)
Ahm*_*han 11
你不应该使用 AtomicInteger,你不应该使用东西,除非你有很好的理由使用。使用 AtomicInteger 的原因可能是只允许并发访问等。
当涉及到您的问题时;
Holder 可用于在 lambda 中保持和递增它。然后你可以通过调用 runCount.value 得到它
Holder<Integer> runCount = new Holder<>(0);
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
runCount.value++; // now it's work fine!
});
System.out.println("The lambda ran " + runCount + " times");
Run Code Online (Sandbox Code Playgroud)
对我来说,这成功了,希望有人觉得它有用:
AtomicInteger runCount = new AtomicInteger(0);
myStream.stream().filter(...).forEach(item -> runCount.getAndIncrement());
System.out.println("The lambda ran " + runCount.get() + "times");
Run Code Online (Sandbox Code Playgroud)
getAndIncrement()
Java 文档指出:
原子地递增当前值,具有 VarHandle.getAndAdd 指定的内存效果。等效于 getAndAdd(1)。
如果您不想创建字段,因为您只在本地需要它,则可以将其存储在匿名类中:
int runCount = new Object() {
int runCount = 0;
{
myStream.stream()
.filter(...)
.peek(x -> runCount++)
.forEach(...);
}
}.runCount;
Run Code Online (Sandbox Code Playgroud)
很奇怪,我知道。但它确实使临时变量超出了本地范围。
小智 5
另一种选择是使用 apache commons MutableInt。
MutableInt cnt = new MutableInt(0);
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
cnt.increment();
});
System.out.println("The lambda ran " + cnt.getValue() + " times");
Run Code Online (Sandbox Code Playgroud)