通过谓词查找第一个元素

我刚刚开始使用Java 8 lambdaexpression式,并试图在函数式语言中实现一些我习惯的东西。

例如,大多数函数式语言都具有某种查找函数,它可以对序列进行操作,或者列表返回第一个元素,谓词为true 。 我可以看到在Java 8中实现这一点的唯一方法是:

 lst.stream() .filter(x -> x > 5) .findFirst() 

然而,这对我来说似乎是无效的,因为filter将扫描整个列表,至less在我的理解(这可能是错误的)。 有没有更好的办法?

不,filter不会扫描整个stream。 这是一个中间操作,它返回一个懒惰的stream(实际上所有的中间操作返回一个懒惰的stream)。 为了说服你,你可以简单地做下面的testing:

 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 

你会发现实际上只处理了stream的两个第一个元素。

所以你可以用你的方法,这是完全正确的。

然而,这对我来说似乎是低效的,因为filter将扫描整个列表

不,它不会 – 只要find满足谓词的第一个元素就会“中断”。 你可以阅读更多关于stream包javadoc懒惰,特别是(重点我的):

许多stream操作(例如过滤,映射或重复删除)可以被懒惰地实现,从而为优化提供机会。 例如,“查找具有三个连续元音的第一个string”不需要检查所有的inputstring。 stream操作分为中间(stream生产)操作和terminal(价值或副作用生产)操作。 中间业务总是懒散的。

 return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null); 

我不得不从对象列表中只滤出一个对象。 所以我用这个,希望它有帮助。

除非你的列表真的很庞大 (数以千计的元素),否则在这里使用stream只是昂贵的,甚至使代码更难理解。

注意:java不是一个函数式语言(而且jvm并不特别适合有效地实现函数式语言)。

更简单和更有效率(在所有的Iterable的):

 for (MyType walk : lst) if (walk > 5) { do_whatever; break; } 

或者,如果你想跳过迭代器:

 for (int x=0; x<list.size(); x++) if (list.get(x) > 5 { do_whatever; break; } 

其实,我真的很想知道为什么有些人会提出这种复杂而昂贵的数据stream机制,即使是像数组的第一个元素那样的微不足道的事情。 (是的:Java8中仍然支持数组)。