Java8之stream流

简介: 流是什么流是Java8 API的新功能,它允许使用声明性方式处理集合。可以将其看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理。例如下面这行代码: // 利用多核架构并行处理 menus.

流是什么

流是Java8 API的新功能,它允许使用声明性方式处理集合。可以将其看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理。

例如下面这行代码:

    // 利用多核架构并行处理
    menus.parallelStream()
        // 选出400卡路里以下的菜
        .filter(dish -> dish.getCalories() < 400)
        // 按照卡路里排序
        .sorted(Comparator.comparing(Dish::getCalories))
        // 提取菜名
        .map(Dish::getName)
        // 只选择头三个
        .limit(3L)
        // 将结果保存在List集合里
        .collect(Collectors.toList())
        // 打印结果
        .forEach(System.out::println);

流的优点:

  1. 代码是以声明性方式写的:说明想要完成什么而非说明如何实现该操作。
  2. 可以将多个基础操作链接起来,以此来表达复杂的数据处理流水线,同时保持代码清晰可读。
menus=>start: menus
List=>end: List
filter=>subroutine: filter
sorted=>subroutine: sorted
map=>subroutine: map

menus(right)->filter(right)->sorted(right)->map(right)->List


流的定义

简单来说,流就是从支持数据处理操作的源生成的元素序列。

  • 元素序列:就像集合一样,流提供一个接口,通过该接口可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素。但流的目的在于表达计算
  • 源:流会使用一个提供数据的源,这个源可以是集合、数组或输入资源。若从有序集合生成流时会保留原有的顺序。
  • 数据处理操作:流的数据处理功能支持类似数据库的操作,以及函数式编程语言中的常用操作,如filtermapreducefindmatchsort等。流操作可以顺序执行,也可以并行执行。

流的特点

  1. 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。流水线的操作可以看作对数据源进行数据库式的查询。
  2. 内部迭代——与使用迭代器显示迭代的集合不同,流的迭代操作是在背后进行的。

流的内部优化

我们看一段代码:

menu.stream()
//        筛选出不是蔬菜的食物
        .filter(dish -> {
          System.out.println(dish.toString());
          System.out.println("------------this is filter--------------");
          return !dish.getVegetarian();
        })
//        将对象转换为string类型
        .map(dish->{
          System.out.println(dish.toString());
          System.out.println("------------this is map--------------");
          return dish.toString();
        })
//        只获取四个
        .limit(4L)
        .collect(Collectors.toSet())
        .forEach(System.out::println);

打印在控制台的结果让人惊叹——

Dish(name=pork, vegetarian=false, calories=800, type=MEAT)
------------this is filter--------------
Dish(name=pork, vegetarian=false, calories=800, type=MEAT)
------------this is map--------------
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
------------this is filter--------------
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
------------this is map--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
------------this is filter--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
------------this is map--------------
Dish(name=french fries, vegetarian=true, calories=530, type=OTHER)
------------this is filter--------------
Dish(name=rice, vegetarian=true, calories=350, type=OTHER)
------------this is filter--------------
Dish(name=season fruit, vegetarian=true, calories=120, type=OTHER)
------------this is filter--------------
Dish(name=pizza, vegetarian=true, calories=550, type=OTHER)
------------this is filter--------------
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
------------this is filter--------------
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
------------this is map--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
Dish(name=pork, vegetarian=false, calories=800, type=MEAT)

这些优化用到了流的延迟性质。尽管filtermap是两个独立的操作,但是它们合并到同一次遍历中。

流的使用

  • filter方法:该方法会接受一个boolean类型结果的函数,并返回一个符合条件的流。

例如:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        筛选出偶数
        .filter(num -> num % 2 == 0)
        .forEach(System.out::println);

// result: 2, 4, 6, 6, 4, 2
  • distinct方法:该方法会去除流中重复的元素(根据流所生成元素的hashCodeequals方法实现)。

例如:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        去除重复的元素
        .distinct()
        .forEach(System.out::println);

// result: 1, 2, 3, 4, 5, 6, 7
  • limit方法:该方法会返回一个不超过给定长度的流。

例如:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        获取前5个对象
        .limit(5L)
        .forEach(System.out::println);

// result: 1, 2, 3, 4, 5
  • skip方法:该方法会返回一个扔掉了前n个元素的流。

例如:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        扔掉前5个对象
        .skip(5L)
        .forEach(System.out::println);

// result: 6, 7, 6, 5, 4, 3, 2, 1
  • map方法:该方法会对每个元素进行函数运算,并将其映射为一个新的元素(返回函数运算结果)。

例如(对每个元素进行函数运算):

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        对每个元素进行统一的操作
        .map(num -> ++num)
        .forEach(System.out::println);

// result: 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2

例如(对集合内某一元素进行提取:

List<Dish> menu = Arrays.asList(
        new Dish("pork", false, 800, Type.MEAT),
        new Dish("beef", false, 700, Type.MEAT),
        new Dish("chick", false, 400, Type.MEAT),
        new Dish("french fries", true, 530, Type.OTHER),
        new Dish("rice", true, 350, Type.OTHER),
        new Dish("season fruit", true, 120, Type.OTHER)
    );
//    提取每道菜的名字
    List<String> dishNames = menu.stream()
        .map(Dish::getName)
        .collect(Collectors.toList());
    dishNames.forEach(System.out::println);

//result: pork, beef, chick, french fries, rice, season fruit
  • flatMap方法:该方法会将一个流中的每个元素转换为流,一个元素对应一个流,然后把所有的流再连接起来成为一个流。

例如:

List<List<String>> lists = Arrays.asList(
        Arrays.asList("1", "2", "3"),
        Arrays.asList("4", "5", "6"),
        Arrays.asList("7", "8", "9")
    );
    List<String> strings = lists.stream()
//        将每个集合都转换为数组
        .map(List::toArray)
//        将每个数组内的元素转换为流
        .flatMap(Arrays::stream)
        .map(Object::toString)
        .collect(Collectors.toList());
    strings.forEach(System.out::println);

// result: "1", "2", "3", "4", "5", "6", "7", "8", "9"
  • anyMatch方法:该方法会判断流中是否有至少一个元素能匹配给定的判断条件。

例如:

//    判断菜谱里是否有蔬菜
    boolean isVegetarian = menu.stream().anyMatch(Dish::getVegetarian);
  • allMatch方法:该方法会判断流中是否任何元素都能匹配给定的判断条件。

例如:

//    判断菜谱里是否全部都是蔬菜
    boolean isVegetarian = menu.stream().allMatch(Dish::getVegetarian);
  • noneMatch方法:该方法会判断流中是否任何元素都不匹配给定的判断条件。

例如:

//    判断菜谱里是否没有蔬菜
    boolean isVegetarian = menu.stream().noneMatch(Dish::getVegetarian);
  • findAny方法:该方法会返回流中的任意元素。

例如:

Optional<Dish> optionalDish = menu.stream()
        .filter(Dish::getVegetarian)
        .findAny();
//    是否存在蔬菜类食物
    boolean isVegetarian = optionalDish.isPresent();
    if (isVegetarian){
//    如果存在则输出该食物信息
      System.out.println(optionalDish.get().toString());
      return;
    }
    System.out.println("not vegetarian in menu");
  }
  • findFirst方法:该方法会返回流中的地一个元素。

例如:

Optional<Integer> optionalInteger = Stream.of(1, 2, 3, 4, 5, 6)
        .filter(num -> num % 3 == 0)
        .findFirst();
    boolean isHave = optionalInteger.isPresent();
    if (isHave) {
      System.out.println(optionalInteger.get().toString());
      return;
    }
    System.out.println("not num");
  }
  • reduce方法:该方法有接收标识符符合BinaryOperator<T>的表达式,将两个元素结合起来返回一个新的值。

例如:

Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//    求和
    Integer sum = integerStream.reduce(Integer::sum).get();
//    最大值
    Integer max = integerStream.reduce(Integer::max).get();
//    最小值
    Integer min = integerStream.reduce(Integer::min).get();
    ......
目录
相关文章
|
12天前
|
安全 Java 大数据
|
12天前
|
Java Unix Windows
|
6天前
|
分布式计算 Java API
Java 8新特性之Lambda表达式与Stream API
【5月更文挑战第1天】本文将介绍Java 8中的两个重要特性:Lambda表达式和Stream API。Lambda表达式是一种新的函数式编程语法,可以简化代码并提高可读性。Stream API是一种用于处理集合的新工具,可以方便地进行数据操作和转换。通过结合Lambda表达式和Stream API,我们可以更加简洁高效地编写Java代码。
|
7天前
|
Java
Java8 Stream Collectors groupingBy使用
Java8 Stream Collectors groupingBy使用
|
10天前
|
Java API 开发者
【专栏】Java 8新特性之Stream API详解
【4月更文挑战第27天】Java 8的Stream API是处理集合数据的新方式,强调简洁和声明式编程。它基于延迟执行和惰性求值,提供创建、中间操作(如filter、map)和终端操作(如forEach、collect)。示例展示了如何通过Stream排序、过滤、映射和聚合数据。
|
11天前
|
Java 编译器 API
Java基础教程(17)-Java8中的lambda表达式和Stream、Optional
【4月更文挑战第17天】Lambda表达式是Java 8引入的函数式编程特性,允许函数作为参数或返回值。它有简洁的语法:`(parameters) -> expression 或 (parameters) ->{ statements; }`。FunctionalInterface注解用于标记单方法接口,可以用Lambda替换。
|
12天前
|
SQL Java
【java高级】stream流的基本用法(一)
【java高级】stream流的基本用法(一)
6 0
|
12天前
|
存储 SQL Java
Java8 Stream API 详解:流式编程进行数据处理
Java8 Stream API 详解:流式编程进行数据处理
|
12天前
|
存储 SQL Java
JavaSE&Java8 Stream
JavaSE&Java8 Stream
12 1
|
13天前
|
存储 SQL Java
Java8 Stream
Java8 Stream
10 0