图解Java8 Stream常用方法

Stream流是Java8新增的一个特性,可以让你以声明的方式处理数据,像SQL语句一样,对数据进行筛选、排序和聚合等操作,而不对源数据进行更改。

Stream API可以简洁我们的代码,大大提升可读性。 咱们先看几个方法感受一下。

Stream.forEach()

看方法名,大概能猜出来,是对流进行遍历。其方法签名void forEach(Consumer<? super E> action),作用是对容器中的每个元素执行action指定的动作。

public static void main(String[] args) {
    List<User> userList = getUserList();
    // userId=1
    // userId=2
    // userId=3
    userList.forEach(e -> System.out.println("userId=" + e.getUserId()));
}

private static List<User> getUserList() {
    return Arrays.asList(new User().setUserId(1), 
             new User().setUserId(2), 
             new User().setUserId(3));
}
复制代码

Stream.map()

可以理解为是一个转换函数,对每个元素按照某种操作进行转换。 Stream-map.png

比如需要把List转化成List,然后返给前端展示:

Java8之前

List<User> userList = getUserList();

List<UserVO> userVOList = new ArrayList<>();
for (User user : userList) {
    UserVO userVO = new UserVO();
    userVO.setUserId(user.getUserId());
    // 省略set方法
    userVOList.add(userVO);
}
复制代码

使用Java8 Stream

List<User> userList = getUserList();

List<UserVO> userVOList = userList.stream().map(user -> {
    UserVO userVO = new UserVO();
    userVO.setUserId(user.getUserId());
    // 省略set方法
    return userVO;
}).collect(Collectors.toList());
复制代码
  • .stream()将一个集合转换为Stream流
  • .collect()用于结果的收集
  • Collectors 收集器将流转换成集合和聚合元素

与Java8之前的区别在于,省略了new ArrayList<>()的过程,使用map函数,将User映射为UserVO。

Stream.filter()

函数的签名为Stream<T> filter(Predicate<? super T> predicate),作用是返回一个只包含满足predicate条件元素的Stream。也就是说filter是一个过滤函数。

这里要解释一下predicate,其中文意思是谓语,是一个布尔值函数,可以理解为条件。 Stream-filter.png 比如需要获取集合中userId为奇数的集合:

List<User> userList = getUserList();
userList.stream()
    .filter(user -> user.getUserId() % 2 == 1)
    .forEach(user -> System.out.println("userId=" + user.getUserId()));

// 控制台输出
// userId=1
// userId=3
复制代码

Stream.sorted()

sorted的作用是对流中的元素进行排序排序,它有两个方法签名:Stream sorted()和Stream sorted(Comparator<? super T> comparator),其中前者要求流中的元素需要实现Comparable接口,否则会抛出ClassCastException异常。 Stream-sorted.png 比如把集合按照userId倒序排序:

private static List<User> getUserList() {
    return Arrays.asList(new User().setUserId(1), 
             new User().setUserId(3), 
             new User().setUserId(2));
}

public static void main(String[] args) {
    // User没有实现Comparable接口
    List<User> userList = getUserList();
    userList.stream()
        // Exception in thread "main" java.lang.ClassCastException: 
        // User cannot be cast to java.lang.Comparable
        .sorted()
        .forEach(user -> System.out.println("userId=" + user.getUserId()));
}

public static void main(String[] args) {
    List<User> userList = getUserList();
    userList.stream()
        .sorted((u1, u2) -> u2.getUserId() - u1.getUserId())
        .forEach(user -> System.out.println("userId=" + user.getUserId()));
}

// 控制台输出
// userId=3
// userId=2
// userId=1

复制代码

小结

细心的同学可能会发现,forEach()没有返回值,而map(), filter(), sorted()的返回值还是一个Stream。实际上,Stream的操作可以分为两类:

  • 中间操作(intermediate operations):总是会惰式执行;
  • 结束操作(terminal operations):会触发实际计算。

可以这么理解,返回值为Stream的,都是中间操作。那什么是惰式执行呢?就是说中间操作是不会立即执行的,直到遇到结束操作,才会执行。比如下面一段代码是不会执行的:

Stream.of("a""b""c""d""e")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    }); 
复制代码

这样的话,我们可以通过多个中间操作 + 1个结束操作,对数据进行筛选、排序、转换和收集等操作。

最后,留个思考题:多个中间操作的顺序有什么讲究吗?可以留言谈谈你的想法。

分类:
后端
标签: