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();
    ......
目录
相关文章
|
15天前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
31 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
15天前
|
Java Shell 流计算
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
17 1
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
|
1月前
|
存储 Java API
Java——Stream流详解
Stream流是JDK 8引入的概念,用于高效处理集合或数组数据。其API支持声明式编程,操作分为中间操作和终端操作。中间操作包括过滤、映射、排序等,可链式调用;终端操作则完成数据处理,如遍历、收集等。Stream流简化了集合与数组的操作,提升了代码的简洁性
63 11
Java——Stream流详解
|
15天前
|
存储 Java 数据处理
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
21 1
|
1月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
1月前
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的探索
【9月更文挑战第24天】本文将深入浅出地介绍Java 8中的重要新特性——Lambda表达式和Stream API,通过实例解析其语法、用法及背后的设计哲学。我们将一探究竟,看看这些新特性如何让Java代码变得更加简洁、易读且富有表现力,同时提升程序的性能和开发效率。
|
9天前
|
Java API 数据处理
java Stream详解
【10月更文挑战第4天】
14 0
|
1月前
|
SQL Java Linux
Java 8 API添加了一个新的抽象称为流Stream
Java 8 API添加了一个新的抽象称为流Stream
|
1月前
|
Java 大数据 API
Java8的stream里的并行度如何使用?效率有提升吗?
Java8的stream里的并行度如何使用?效率有提升吗?
20 4
|
18天前
|
存储 分布式计算 Java
Stream很好,Map很酷,但答应我别用toMap():Java开发中的高效集合操作
在Java的世界里,Stream API和Map集合无疑是两大强大的工具,它们极大地简化了数据处理和集合操作的复杂度。然而,在享受这些便利的同时,我们也应当警惕一些潜在的陷阱,尤其是当Stream与Map结合使用时。本文将深入探讨Stream与Map的优雅用法,并特别指出在使用toMap()方法时需要注意的问题,旨在帮助大家在工作中更高效、更安全地使用这些技术。
28 0