繁体   English   中英

我如何使用 Stream Java 8 按元素比较两个列表?

[英]How can i compare two lists by elements using Stream Java 8?

这是一些网站的简单任务。

如果 a[i] > b[i],则爱丽丝获得 1 分。 如果 a[i] < b[i],则 Bob 获得 1 分。 如果 a[i] = b[i],那么两个人都得不到分数。 返回:Alice的分数在第一位,Bob的分数在第二位。

我可以使用流来编写这段代码吗?

public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
    int alice = 0;
    int bob = 0;
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) > b.get(i)) 
            alice++;
        else if (a.get(i) < b.get(i)) 
            bob++;
    }
    return Arrays.asList(alice, bob);
}

...

经过一番思考,我写了这段代码:

public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
    Integer[] result = {0, 0};
    IntStream.range(0, Math.min(a.size(), b.size()))
            .map(i -> a.get(i).compareTo(b.get(i)))
            .forEach(i -> {
                if (i > 0) result[0]++;
                else if (i < 0) result[1]++;
            });
    return Arrays.asList(result);
}

但是这段代码并没有简洁多少。 有没有办法让代码更漂亮?

您可以使用filtercount使代码更短,但在这里使用循环更简洁。 Stream并不总是解决方案。

return Arrays.asList(
    IntStream.range(0, Math.min(a.size(), b.size())).filter(i -> a.get(i) > b.get(i)).count(),
    IntStream.range(0, Math.min(a.size(), b.size())).filter(i -> a.get(i) < b.get(i)).count()
);

Java 8 - partitioningBy() & counting()

解决流只对给定数据集执行一次迭代的问题的方法之一是利用内置的收集器partitioningBy()counting()

public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
    
    return IntStream.range(0, a.size())
        .map(i -> a.get(i) - b.get(i))
        .filter(i -> i != 0)
        .boxed()
        .collect(Collectors.collectingAndThen(
            Collectors.partitioningBy(i -> i > 0, Collectors.counting()),
            map -> Arrays.asList(map.get(true).intValue(), map.get(false).intValue())
        ));
}

Java 8 - 自定义Collector

另一种选择是使用静态工厂方法Collector.of()定义自定义 Collector。 与以前的方法一样,它允许使用单个流处理数据:

public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
    
    return IntStream.range(0, a.size())
        .boxed()
        .collect(Collector.of(
            () -> new int[]{0, 0},
            (int[] score, Integer i) -> {
                if (a.get(i) > b.get(i)) score[0]++;
                if (b.get(i) > a.get(i)) score[1]++;
            },
            (int[] left, int[] right) -> {
                Arrays.setAll(left, i -> left[i] + right[i]);
                return left;
            },
            arr -> Arrays.asList(arr[0], arr[1])
        ));
}

Java 12 - 收集器teeing()

另一个允许使用单个流生成结果的选项是 Java 12 Collector teeing() ,它需要两个下游收集器和一个函数,该函数通过合并它们生成的结果来执行最终转换。

public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
    
    return IntStream.range(0, a.size())
        .boxed()
        .collect(Collectors.teeing(
            Collectors.filtering(i -> a.get(i) > b.get(i), Collectors.counting()),
            Collectors.filtering(i -> b.get(i) > a.get(i), Collectors.counting()),
            (alice, bob) -> List.of(alice.intValue(), bob.intValue())
        ));
}

这对你的情况来说可能有点矫枉过正,但你可以将其作为流减少来处理:

class Counter {
    private int bob = 0;
    private int alice = 0;

    public void accept(int index) {
        if (a[index] > b[index])
            alice++;
        else if (a[index] < b[index])
            bob++;
    }
}

Counter counter = IntStream.range(0, a.size()).boxed().reduce(new Counter(), Counter::accept);

另一种选择是利用Integer.compare

List<Integer> comparisons = IntStream.range(0, a.size()).map(i -> Integer.compare(a[i], b[i]).toList();
long bob = comparisons.stream().filter(n -> n < 0).count();
int alice = comparisons.stream().filter(n -> n > 0).count();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM