我刚刚开始使用Java 8 lambdas,我正在尝试实现一些我在函数式语言中习惯的东西。
例如,大多数函数式语言都有某种find函数,它对序列或列表进行操作,返回谓词为真的第一个元素。我认为在Java 8中实现这一目标的唯一方法是:
lst.stream()
.filter(x -> x > 5)
.findFirst()
然而,这似乎对我来说效率很低,因为过滤器会扫描整个列表,至少在我的理解(这可能是错误的)。有没有更好的办法?
我刚刚开始使用Java 8 lambdas,我正在尝试实现一些我在函数式语言中习惯的东西。
例如,大多数函数式语言都有某种find函数,它对序列或列表进行操作,返回谓词为真的第一个元素。我认为在Java 8中实现这一目标的唯一方法是:
lst.stream()
.filter(x -> x > 5)
.findFirst()
然而,这似乎对我来说效率很低,因为过滤器会扫描整个列表,至少在我的理解(这可能是错误的)。有没有更好的办法?
当前回答
对于我来说,一个带有循环的通用实用函数似乎要干净得多:
static public <T> T find(List<T> elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
static public <T> T find(T[] elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
在使用:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Integer[] intArr = new Integer[]{1, 2, 3, 4, 5};
System.out.println(find(intList, i -> i % 2 == 0)); // 2
System.out.println(find(intArr, i -> i % 2 != 0)); // 1
System.out.println(find(intList, i -> i > 5)); // null
其他回答
@AjaxLeung已经回复了,但在评论中很难找到。 仅供支票使用
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
化简为
lst.stream()
.anyMatch(x -> x > 5)
然而,这对我来说似乎效率很低,因为过滤器会扫描整个列表
不,它不会——一旦找到满足谓词的第一个元素,它就会“中断”。你可以在流包javadoc中读到更多关于懒惰的内容,特别是(强调我的):
许多流操作(如过滤、映射或重复删除)可以延迟实现,从而为优化提供了机会。例如,“找到带有三个连续元音的第一个字符串”不需要检查所有输入字符串。流操作分为中间(产生流)操作和终端(产生价值或副作用)操作。中间操作总是懒惰的。
不,过滤器不扫描整个流。它是一个中间操作,返回一个惰性流(实际上所有中间操作都返回一个惰性流)。为了说服你,你可以简单地做下面的测试:
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.get();
System.out.println(a);
输出:
will filter 1
will filter 10
10
您可以看到,实际上只处理了流的前两个元素。
所以你可以用你的方法,这是完全没问题的。
对于我来说,一个带有循环的通用实用函数似乎要干净得多:
static public <T> T find(List<T> elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
static public <T> T find(T[] elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
在使用:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Integer[] intArr = new Integer[]{1, 2, 3, 4, 5};
System.out.println(find(intList, i -> i % 2 == 0)); // 2
System.out.println(find(intArr, i -> i % 2 != 0)); // 1
System.out.println(find(intList, i -> i > 5)); // null
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
// Stream is ~30 times slower for same operation...
public class StreamPerfTest {
int iterations = 100;
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
// 55 ms
@Test
public void stream() {
for (int i = 0; i < iterations; i++) {
Optional<Integer> result = list.stream()
.filter(x -> x > 5)
.findFirst();
System.out.println(result.orElse(null));
}
}
// 2 ms
@Test
public void loop() {
for (int i = 0; i < iterations; i++) {
Integer result = null;
for (Integer walk : list) {
if (walk > 5) {
result = walk;
break;
}
}
System.out.println(result);
}
}
}