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();
    ......
目录
相关文章
|
1月前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
57 6
|
1月前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
26天前
|
Rust 安全 Java
Java Stream 使用指南
本文介绍了Java中Stream流的使用方法,包括如何创建Stream流、中间操作(如map、filter、sorted等)和终结操作(如collect、forEach等)。此外,还讲解了并行流的概念及其可能带来的线程安全问题,并给出了示例代码。
|
2月前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
28 0
|
3月前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
51 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
3月前
|
Java Shell 流计算
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
30 1
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
|
2月前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
3月前
|
存储 Java 数据处理
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
50 1
|
4月前
|
SQL Java Linux
Java 8 API添加了一个新的抽象称为流Stream
Java 8 API添加了一个新的抽象称为流Stream
|
3月前
|
Java API 数据处理
java Stream详解
【10月更文挑战第4天】
43 0