【发布时间】:2014-08-16 01:31:30
【问题描述】:
给定A 类型的对象列表和boolean foo(A other); 方法,使用Java 8 lambda 查找任何对象对(例如a1.foo(a2) == true)的最佳方法是什么?
注意方法foo是对称的,意思是a1.foo(a2) == a2.foo(a1)(如equals)。
另外,如果我只需要知道任何 a1 而不是 (a1, a2) 对,那么最好的方法是什么?
例如:
假设我有一个学生列表,我想在列表中找到与其他学生年龄相同的任何学生。
下面的方法Person.sameAgeAs(Person other)返回true/false,用于比较他们的年龄:
for (Person student1 : students) {
for (Person student2 : students) {
if (!student1.equals(student2)) { // If not the same student.
if (student1.sameAgeAs(student2)) { // If they have the same age.
return student1;
}
}
}
}
但我想用 lambdas 来做:
students.stream()...
编辑:
如果方法 foo 是 not 对称的,那么 @srborlongan 下面的答案将是完美的。将其封装在 Pair 类中并接受 predicate:
@NotNull
public static <I> Pair<I, I> findPair(@Nullable List<I> list, @NotNull BiPredicate<I, I> predicate) {
if (list == null) return emptyPair();
Optional<Pair<I,I>> item = list.parallelStream()
.flatMap(item1 ->
list.stream()
.filter(item2 -> !item1.equals(item2))
.filter(item2 -> predicate.test(item1, item2))
.map(item2 -> new Pair<>(item1, item2))).findFirst();
return item.orElse(emptyPair());
}
但是,正如我所说,foo 是对称的,上面的代码效率低下,因为: (i) 如果存在答案,则可能会在所有非对称对之前搜索一些对称对; (ii) 如果不存在答案,即student1.sameAgeAs(student2) 永远不会返回true,此代码将搜索所有对,包括对称对。为了提高效率,代码应该只检查一半的对(因为student1.sameAgeAs(student2) == student2.sameAgeAs(student1))。
我最初的for (Person ... 示例并不好,因为它还会搜索所有对。这确实是应该被翻译成 lambdas 才能高效:
@NotNull
public static <I> Pair<I, I> findPair_Symmetric(List<I> list, BiPredicate<I, I> predicate_Symmetric) {
if (list != null) {
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if ((i != j) && predicate_Symmetric.test(list.get(i), list.get(j))) {
return new Pair<>(list.get(i), list.get(j));
}
}
}
}
return emptyPair();
}
【问题讨论】:
-
这个算法是 O(n^2),但是如果你可以有一个比较函数而不是 BiPredicate,并且使用一个 TreeSet,那么它可以变成一个 O(nlogn)——而不是你只填充树,直到你发现它已经包含一个等价的对象。此外,如果您可以散列匹配的属性 - 您可以使用 HashSet 并获得 O(n) 性能。
标签: algorithm lambda java-8 java-stream