Java 8 Lambda 在递增计数时的有效最终问题
Issue with Java 8 Lambda for effective final while incrementing counts
我想在以下场景中使用 Java 8 Lambda 表达式,但我得到 在封闭范围内定义的局部变量 fooCount 必须是最终的或实际上是最终的。我明白错误消息的意思,但我需要在这里计算百分比,所以需要递增 fooCount
和 barCount
然后计算百分比。那么有什么方法可以实现呢:
// key is a String with values like "FOO;SomethinElse" and value is Long
final Map<String, Long> map = null;
....
private int calculateFooPercentage() {
long fooCount = 0L;
long barCount = 0L;
map.forEach((k, v) -> {
if (k.contains("FOO")) {
fooCount++;
} else {
barCount++;
}
});
final int fooPercentage = 0;
//Rest of the logic to calculate percentage
....
return fooPercentage;
}
我有一个选择是在这里使用 AtomicLong
而不是 long
但我想避免它,所以以后如果可能的话我想在这里使用并行流。
流中有一个 count
方法可以为您计数。
long fooCount = map.keySet().stream().filter(k -> k.contains("FOO")).count();
long barCount = map.size() - fooCount;
如果要并行化,请更改 .stream()
to .parallelStream()
。
或者,如果您尝试手动递增一个变量,并使用流并行化,那么您可能希望使用 AtomicLong
之类的东西来保证线程安全。一个简单的变量,即使编译器允许,也不是线程安全的。
要获取数字、匹配和非匹配元素,您可以使用
Map<Boolean, Long> result = map.keySet().stream()
.collect(Collectors.partitioningBy(k -> k.contains("FOO"), Collectors.counting()));
long fooCount = result.get(true);
long barCount = result.get(false);
但是由于你的源是一个 Map
,它知道它的总大小,并且想要计算一个百分比,对此 barCount
是不需要的,这个特定的任务可以解决为
private int calculateFooPercentage() {
return (int)(map.keySet().stream().filter(k -> k.contains("FOO")).count()
*100/map.size());
}
两种变体都是线程安全的,即将 stream()
更改为 parallelStream()
将并行执行操作,但是,此操作不太可能受益于并行处理。您将需要大量的密钥字符串或地图才能获得收益……
我同意其他答案,您应该使用 count
或 partitioningBy
。
为了举例说明原子性问题,请考虑以下代码:
private static AtomicInteger i1 = new AtomicInteger(0);
private static int i2 = 0;
public static void main(String[] args) {
IntStream.range(0, 100000).parallel().forEach(n -> i1.incrementAndGet());
System.out.println(i1);
IntStream.range(0, 100000).parallel().forEach(n -> i2++);
System.out.println(i2);
}
returns i1
的预期结果为 100000,但 i2
的预期结果小于此值(在我的测试运行中介于 50000 和 80000 之间)。原因应该很明显了。
我想在以下场景中使用 Java 8 Lambda 表达式,但我得到 在封闭范围内定义的局部变量 fooCount 必须是最终的或实际上是最终的。我明白错误消息的意思,但我需要在这里计算百分比,所以需要递增 fooCount
和 barCount
然后计算百分比。那么有什么方法可以实现呢:
// key is a String with values like "FOO;SomethinElse" and value is Long
final Map<String, Long> map = null;
....
private int calculateFooPercentage() {
long fooCount = 0L;
long barCount = 0L;
map.forEach((k, v) -> {
if (k.contains("FOO")) {
fooCount++;
} else {
barCount++;
}
});
final int fooPercentage = 0;
//Rest of the logic to calculate percentage
....
return fooPercentage;
}
我有一个选择是在这里使用 AtomicLong
而不是 long
但我想避免它,所以以后如果可能的话我想在这里使用并行流。
流中有一个 count
方法可以为您计数。
long fooCount = map.keySet().stream().filter(k -> k.contains("FOO")).count();
long barCount = map.size() - fooCount;
如果要并行化,请更改 .stream()
to .parallelStream()
。
或者,如果您尝试手动递增一个变量,并使用流并行化,那么您可能希望使用 AtomicLong
之类的东西来保证线程安全。一个简单的变量,即使编译器允许,也不是线程安全的。
要获取数字、匹配和非匹配元素,您可以使用
Map<Boolean, Long> result = map.keySet().stream()
.collect(Collectors.partitioningBy(k -> k.contains("FOO"), Collectors.counting()));
long fooCount = result.get(true);
long barCount = result.get(false);
但是由于你的源是一个 Map
,它知道它的总大小,并且想要计算一个百分比,对此 barCount
是不需要的,这个特定的任务可以解决为
private int calculateFooPercentage() {
return (int)(map.keySet().stream().filter(k -> k.contains("FOO")).count()
*100/map.size());
}
两种变体都是线程安全的,即将 stream()
更改为 parallelStream()
将并行执行操作,但是,此操作不太可能受益于并行处理。您将需要大量的密钥字符串或地图才能获得收益……
我同意其他答案,您应该使用 count
或 partitioningBy
。
为了举例说明原子性问题,请考虑以下代码:
private static AtomicInteger i1 = new AtomicInteger(0);
private static int i2 = 0;
public static void main(String[] args) {
IntStream.range(0, 100000).parallel().forEach(n -> i1.incrementAndGet());
System.out.println(i1);
IntStream.range(0, 100000).parallel().forEach(n -> i2++);
System.out.println(i2);
}
returns i1
的预期结果为 100000,但 i2
的预期结果小于此值(在我的测试运行中介于 50000 和 80000 之间)。原因应该很明显了。