Java 8 Lambda 在递增计数时的有效最终问题

Issue with Java 8 Lambda for effective final while incrementing counts

我想在以下场景中使用 Java 8 Lambda 表达式,但我得到 在封闭范围内定义的局部变量 fooCount 必须是最终的或实际上是最终的。我明白错误消息的意思,但我需要在这里计算百分比,所以需要递增 fooCountbarCount 然后计算百分比。那么有什么方法可以实现呢:

        // 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() 将并行执行操作,但是,此操作不太可能受益于并行处理。您将需要大量的密钥字符串或地图才能获得收益……

我同意其他答案,您应该使用 countpartitioningBy

为了举例说明原子性问题,请考虑以下代码:

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 之间)。原因应该很明显了。