我刚开始尝试使用Java 8 lambda表达式,并试图实现一些我在函数式语言中习惯的东西。
例如,大多数函数式语言都有一些基于序列或列表操作的查找函数,返回满足谓词条件的第一个元素。在Java 8中,我唯一能想到的实现方式是:
lst.stream()
.filter(x -> x > 5)
.findFirst()
然而,这对我来说似乎是低效的,因为过滤器将会扫描整个列表,至少在我的理解中是这样(我可能是错误的)。是否有更好的方法?
我刚开始尝试使用Java 8 lambda表达式,并试图实现一些我在函数式语言中习惯的东西。
例如,大多数函数式语言都有一些基于序列或列表操作的查找函数,返回满足谓词条件的第一个元素。在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);
输出结果为:
will filter 1
will filter 10
10
你会发现,流中只有前两个元素被实际处理了。
所以你可以采用你的方法,完全没有问题。
get();
是因为我知道我向流管道提供了哪些值,因此会有结果。实际上,您不应使用get();
,而应使用orElse()
/ orElseGet()
/ orElseThrow()
(以获取更有意义的错误而不是NSEE),因为您可能不知道应用于流管道的操作是否会产生元素。 - Alexis C.orElse
? - John HenckelOptional
类型,因为这是.findFirst
返回的类型。Optional
的一个用途是帮助开发人员避免处理null
。例如,您可以检查myOptional.isPresent()
而不是检查myObject != null
,或者使用Optional
接口的其他部分。这样说清楚了吗? - Alexander Terp不会的 - 只要找到第一个满足谓词的元素,它就会“中断”。您可以在stream package javadoc中阅读有关惰性的更多信息,特别是(重点是我的):
许多流操作(例如过滤、映射或重复删除)都可以实现惰性计算,从而为优化提供机会。例如,“查找具有三个连续元音字母的第一个字符串”不需要检查所有输入字符串。流操作分为中间(生成流)操作和终端(值或副作用生成)操作。中间操作总是惰性的。
return dataSource.getParkingLots()
.stream()
.filter(parkingLot -> Objects.equals(parkingLot.getId(), id))
.findFirst()
.orElse(null);
我需要从一个对象列表中筛选出一个对象。所以我使用了这个方法,希望能帮到你。
.orElse(null) != null
。相反,可以利用 Optional API 的.isPresent
, 即 .findFirst().isPresent()
. - Alexander Terp.stream().map(ParkingLot::getId).anyMatch(Predicate.isEqual(id))
会更加简洁。 - Ozymandiasequals
,但实际上应该是 equal
,所以代码如下:
.filter(parkingLot -> Objects.equal(parkingLot.getId(), id))
- Vibran除了 Alexis C 的回答之外,如果你正在使用一个数组列表,并且你不确定你要搜索的元素是否存在,请使用以下方法。
Integer a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.orElse(null);
那么您可以简单地检查 a 是否为 null
。
已经由@AjaxLeung回答,但是在评论中很难找到。
仅供检查
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
被简化为
lst.stream()
.anyMatch(x -> x > 5)
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);
}
}
}
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
改进的一行代码:如果你想要一个布尔返回值,我们可以通过添加isPresent来更好地实现:
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();