在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附带的任何其他功能来实现此目的?
答案 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循环。我之所以选择这种方法,是因为这样可以使此处的情况更加明显。