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

wnavrhmk  于 2022-12-21  发布在  Java
关注(0)|答案(3)|浏览(253)
  • 这是某个网站上的简单任务 *

如果a[i]〉B[i],则Alice将获得1分。如果a[i]〈b[i],则Bob将获得1分。如果a[i] = b[i],则两个人都不会获得1分。返回:爱丽丝的分数在第一位,鲍勃的分数在第二位。

  • 我可以使用流来编写此代码吗 *
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);
}

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

ejk8hzay

ejk8hzay1#

您可以使用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()
);
r3i60tvu

r3i60tvu2#

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()定义自定义收集器。与之前的方法一样,它允许使用单个流处理数据:

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(),它需要两个下游Collector和一个Function,后者通过合并它们生成的结果来执行最终转换。

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())
        ));
}
0x6upsns

0x6upsns3#

这对于您的情况来说可能有些过分,但您可以将其作为流缩减来处理:

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();

相关问题