Java 通过谓词查找第一个元素
我刚刚开始使用Java8Lambdas,我正在尝试用函数式语言实现一些我习惯的东西 例如,大多数函数式语言都有某种对序列进行操作的find函数,或者返回谓词为Java 通过谓词查找第一个元素,java,java-8,java-stream,Java,Java 8,Java Stream,我刚刚开始使用Java8Lambdas,我正在尝试用函数式语言实现一些我习惯的东西 例如,大多数函数式语言都有某种对序列进行操作的find函数,或者返回谓词为true的第一个元素的列表。在Java 8中实现这一点的唯一方法是: lst.stream() .filter(x -> x > 5) .findFirst() 然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表,至少在我看来是这样(这可能是错误的)。有更好的办法吗 否,过滤器不会扫描整个流。它是一个中间操
true
的第一个元素的列表。在Java 8中实现这一点的唯一方法是:
lst.stream()
.filter(x -> x > 5)
.findFirst()
然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表,至少在我看来是这样(这可能是错误的)。有更好的办法吗 否,过滤器不会扫描整个流。它是一个中间操作,返回一个惰性流(实际上所有中间操作都返回一个惰性流)。要说服您,您只需执行以下测试:
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);
您可以看到,实际上只处理流的前两个元素
所以你可以用你的方法,这是非常好的
然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表
不,它不会-一旦找到满足谓词的第一个元素,它就会“中断”。你可以阅读更多关于懒惰的文章,特别是(我的重点):
许多流操作(如筛选、映射或重复删除)都可以延迟实现,从而提供了优化的机会。例如,“查找包含三个连续元音的第一个字符串”不需要检查所有输入字符串。流操作分为中间(产生流)操作和终端(产生价值或副作用)操作中间操作总是懒惰的。
我必须从对象列表中只筛选出一个对象。所以我使用了这个,希望它能有所帮助。除了用户的答案之外,如果您使用的是数组列表,您不确定要搜索的元素是否存在,请使用这个
Integer a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.orElse(null);
然后,您只需检查a是否为
null改进的一行回答:如果您要查找布尔返回值,我们可以通过添加isPresent来做得更好:
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();
导入org.junit.Test;
导入java.util.array;
导入java.util.List;
导入java.util.Optional;
//对于相同的操作,流速度大约慢30倍。。。
公共类流性能测试{
int迭代次数=100;
List=Arrays.asList(1,10,3,7,5);
//55毫秒
@试验
公共空流(){
对于(int i=0;ix>5)
.findFirst();
System.out.println(result.orElse(null));
}
}
//2毫秒
@试验
公共void循环(){
对于(int i=0;i5){
结果=步行;
打破
}
}
系统输出打印项次(结果);
}
}
}
已由@AjaxLeung回答,但在评论中很难找到。
仅供检查
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
简化为
lst.stream()
.anyMatch(x -> x > 5)
这不是低效的,Java8流实现是惰性的,所以过滤器只应用于终端操作。同样的问题:酷。这就是我希望它能做到的。否则的话,这将是一个重大的设计失败。如果您真的想检查列表是否包含这样的元素(而不是从可能的几个元素中挑出第一个),那么.findAny()理论上在并行设置中会更有效,当然也会更清楚地传达这一意图。与简单的forEach循环相比,这将在堆上创建大量对象和几十个动态方法调用。虽然这可能并不总是影响性能测试的底线,但在热点中,避免琐碎地使用流和类似的重量级结构是有区别的。这个答案对我来说更具信息性,并且解释了为什么,而不仅仅是如何。我从来没有新的中间操作总是懒惰;Java流继续让我感到惊讶代码>在这里,因为我知道我将哪些值馈送到流管道,因此会有结果。实际上,您不应该使用get()
,但是orElse()
/orElseGet()
/orelsetrow()
(用于更有意义的错误而不是NSEE),因为您可能不知道应用于流管道的操作是否会产生元素。.findFirst().orElse(null)代码>例如,不要使用orElse null。这应该是一种反模式。所有这些都包含在可选选项中,那么为什么您要冒NPE的风险呢?我认为处理选择题是更好的方法。在使用可选项之前,请先用isPresent()测试它。@BeJay我不明白。我应该用什么来代替orElse
?@JohnHenckel我认为BeJay的意思是你应该把它作为可选的
类型,这就是.findFirst
返回的。Optional的一个用途是帮助开发人员避免处理null
s。e、 g.不检查myObject!=null
,您可以选中myOptional.isPresent()
,或使用可选界面的其他部分。这清楚了吗?你应该修正你的例子。您不能将null分配给纯整数。我已经编辑了您的帖子。在整数列表中搜索时,0(零)可能是有效结果。将变量类型替换为整数,将默认值替换为null。更好:由于我们正在查找布尔返回值,因此可以通过添加null检查来更好地执行此操作:return dataSource.getParkingLots().stream().filter(parkingLot->Objects.equals(parkingLot.getId(),id)).findFirst().orElse(null)!=无效@shreedharbhat您不需要执行.orElse(null)!=空
。相反,请使用可选API的.isPresent
,即.findFirst().isPresent()
@shr
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);
}
}
}
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
lst.stream()
.anyMatch(x -> x > 5)