有没有一种新方法可以将Java 8中相同列表的元素与lambda进行比较?

时间:2020-03-10 13:28:42

标签: java lambda collections java-8 java-stream

在Java 8中是否可以比较列表中的元素?

我有兴趣重写两个for循环。

private static void test(List<Foo> a) {
    for(int i = 0 ; i < a.size(); i++){
        for(int j = 1; j < a.size() ; j++){
            Foo o1= a.get(i);
            Foo o2= a.get(j);
            if (o1.getFooA().equals(o2.getFooA()) && !o1.getFooB().equals(o2.getFooB()) ) {
                    throw new Exception("some message goes here...");
                }
            }

        }
    }

是否可以使用lambdas / streams或Java 8附带的任何其他功能来实现此目的?

3 个答案:

答案 0 :(得分:3)

不要要求“ lambda /数据流”,而是实际的改进。

使用时

private static void test(List<Foo> a) throws Exception {
    Map<TypeOfFooA, TypeOfFooB> seen = new HashMap<>();
    for(Foo f: a) {
        TypeOfFooB fooB = f.getFooB(), previous = seen.putIfAbsent(f.getFooA(), fooB);
        if(previous != null && !fooB.equals(previous))
            throw new Exception("some message goes here...");
    }
}

您正在单次执行操作,而不是嵌套循环。这使得线性和二次时间复杂度有所不同。比“看起来酷”的影响更大。

您可以将其重写以使用Stream API,例如

private static void test(List<Foo> a) throws Exception {
    Map<TypeOfFooA, TypeOfFooB> seen = a.stream()
        .collect(Collectors.toMap(Foo::getFooA, Foo::getFooB,
            (previous, fooB) -> {
                if(!fooB.equals(previous))
                    throw new RuntimeException("some message goes here...");
                return previous;
            }));
}

但是不建议这样做。收集到随后真正不需要的地图,再结合使用合并/归约功能进行验证并引发异常,可能会使读者感到惊讶,并且仅适用于未经检查的异常。此外,throwing函数无法访问Foo实例和FooA键来构造异常消息。

我建议保持循环。

答案 1 :(得分:2)

这是一个优化的解决方案,但不是很明显:

a.stream()
 .collect(toMap(Foo::getFooA, Foo::getFooB, (x, y) -> {
     if (x.equals(y)) {
         return x;
     } else {
         throw new RuntimeException("some message goes here...");
     }
 }));

它利用了O(1)的{​​{1}}访问时间,从而免除了嵌套循环HashMap的麻烦。


通常,在编程时,您应该同时考虑两个方面:可读性和性能。

您的普通旧Java代码虽然没有经过优化,但可读性很强。在最坏的情况下,它的复杂度为O(n),而使用O(n*n)时,可以将其降低为线性Map

在处理大型列表时,这种区别将非常有意义。

答案 2 :(得分:0)

如果绝对必须使用lambda作为“凉爽”因素。这是一个选择:

private static void test(List<Foo> a) {
    a.forEach(o1 -> {
        if (a.stream().anyMatch(o2 -> o1.getFooA().equals(o2.getFooA()) && !o1.getFooB().equals(o2.getFooB()))) {
            throw new Exception("some message goes here...");
        }
    });
}

它是一个带有嵌套嵌套“ anyMatch”的“ forEach” lambda,而不是两个嵌套的for循环。我之所以选择这种方法,是因为这样可以使此处的情况更加明显。