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

有没有一种新方法可以将Java8中同一列表的元素与lambda进行比较?,java,lambda,collections,java-8,java-stream,Java,Lambda,Collections,Java 8,Java Stream,在Java8中,有没有一种方法可以比较列表中的元素 我对重新编写这两个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);

在Java8中,有没有一种方法可以比较列表中的元素

我对重新编写这两个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中遇到的任何其他功能,是否有一种很酷的方法来实现这一点?

如果您绝对必须使用lambdas作为酷度因子。这里有一个选择:

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...");
        }
    });
}

它不是两个嵌套的for循环,而是一个具有嵌套的anyMatch的forEach lambda,执行相同的操作。我之所以选择这种方法,是因为它使这里发生的事情更加明显。

如果你绝对必须使用lambdas作为凉爽系数。这里有一个选择:

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...");
        }
    });
}

它不是两个嵌套的for循环,而是一个具有嵌套的anyMatch的forEach lambda,执行相同的操作。我之所以选择这种方法,是因为它使这里发生的事情更加明显。

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

a.stream()
 .collect(toMap(Foo::getFooA, Foo::getFooB, (x, y) -> {
     if (x.equals(y)) {
         return x;
     } else {
         throw new RuntimeException("some message goes here...");
     }
 }));
它利用HashMap的O1访问时间,从而避免了复杂的嵌套循环

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

您的普通旧java代码可读性很好,但没有经过优化。在最坏的情况下,它的复杂度为*n,而使用贴图时,可以将其减少为线性


在处理大型列表时,这种差异将非常重要。

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

a.stream()
 .collect(toMap(Foo::getFooA, Foo::getFooB, (x, y) -> {
     if (x.equals(y)) {
         return x;
     } else {
         throw new RuntimeException("some message goes here...");
     }
 }));
它利用HashMap的O1访问时间,从而避免了复杂的嵌套循环

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

您的普通旧java代码可读性很好,但没有经过优化。在最坏的情况下,它的复杂度为*n,而使用贴图时,可以将其减少为线性


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

不要要求“lambdas/streams”,而是要求实际的改进

当你使用

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...");
    }
}
您将在一个过程中执行操作,而不是嵌套循环。这使得线性和二次时间复杂度之间存在差异。这比“看起来很酷”更有影响

您可以重写它以使用流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;
            }));
}
但不推荐这样做。收集到一个事后并不需要的映射,再加上使用merge/reduce函数进行验证并引发异常,可能会让读者感到惊讶,而且它只适用于未检查的异常。此外,抛出函数既不能访问Foo实例也不能访问FooA键来构造异常消息


我建议保持循环。

不要要求“lambdas/streams”,而是实际的改进

当你使用

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...");
    }
}
您将在一个过程中执行操作,而不是嵌套循环。这使得线性和二次时间复杂度之间存在差异。这比“看起来很酷”更有影响

您可以重写它以使用流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;
            }));
}
但不推荐这样做。收集到一个事后并不需要的映射,再加上使用merge/reduce函数进行验证并引发异常,可能会让读者感到惊讶,而且它只适用于未检查的异常。此外,抛出函数既不能访问Foo实例也不能访问FooA键来构造异常消息


我建议继续使用循环。

没有什么比这种传统方法更具可读性的了。请注意,由于比较是对称的,所以比较a和b会产生与b和a相同的结果,但嵌套循环会产生两种比较。因此,这个问题的复杂性可以大大降低。为此,让第二个循环从int j=i开始,而不是int j=1。因此,如果你有像1,1,1,2和2,1,2,2这样的对,对2,1是过时的,因为它已经被1,2覆盖了。另外请注意,您可以跳过案例j==i,因为比较结果总是错误的,所以只需int j=i+1即可。您好@Zabuza,谢谢您的注释,我将谨慎地使用您的建议!您认为您的解决方案是否可以使用lambda或任何Java8特性来禁用?您当然可以使用StreamAPI来实现这一点,但结果很可能不会比这更具可读性,可能更糟。这不适合流API,因为您必须同时遍历两个集合。@Zabuza-这不会改变复杂性。您的优化大约完成了与原始的比较的一半,但N*N和N*N/2都在ON^2复杂度类中。没有什么比这种传统方法更能让代码可读。请注意,您的比较是不必要的,因为比较是对称的,因此,将a与b进行比较将产生与将b与a进行比较相同的结果,但嵌套循环将生成这两种比较。因此,这个问题的复杂性可以大大降低。为此,让第二个循环从int j=i开始,而不是int j=1。如果你有一对像1,1,
1,2和2,1,2,2这对2,1已经过时了,因为它已经被1,2覆盖了。另外请注意,您可以跳过案例j==i,因为比较结果总是错误的,所以只需int j=i+1即可。您好@Zabuza,谢谢您的注释,我将谨慎地使用您的建议!您认为您的解决方案是否可以使用lambda或任何Java8特性来禁用?您当然可以使用StreamAPI来实现这一点,但结果很可能不会比这更具可读性,可能更糟。这不适合流API,因为您必须同时遍历两个集合。@Zabuza-这不会改变复杂性。您的优化与原始优化进行了大约一半的比较,但N*N和N*N/2都在ON^2复杂性类中。如果a.anyMatcho1->a.stream.anyMatcho2->o1.getFooA.equalso2.getFooA&!o1.getFooB.equals2.getFooB{抛出新的异常,一些消息在这里…};如果a.anyMatcho1->a.stream.anyMatcho2->o1.getFooA.equalso2.getFooA&&,那么最好向你的头脑表明你不能在消费者中抛出异常!o1.getFooB.equals2.getFooB{抛出新的异常,一些消息在这里…};最好告诉你不能在消费者中抛出异常…我使用线性循环解决方案作为起点,也发现流变量不是真正推荐的解决方案,类似于你的变量。所以+1对于On解决方案,尽管我仍然推荐了也在循环中的变量。事实上,这并不简单,但很好。我使用线性循环解决方案作为起点,也发现流变量不是真正推荐的解决方案,类似于您的变量。所以+1是一个On解决方案,尽管我仍然建议使用同样On循环的变体。事实上,这不是一个微不足道的问题,尽管很好。