简介

Stream是 Java 8新增加的类,用来补充集合类。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

Stream有以下特性及优点:

  • 无存储。Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
  • 为函数式编程而生。对Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
  • 惰式执行。Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • 可消费性。Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

    Stream的创建

    在Java 8中,可以有多种方法来创建流。
    1、通过已有的集合来创建流
    在Java 8中,除了增加了很多Stream相关的类以外,还对集合类自身做了增强,在其中增加了stream方法,可以将一个集合类转换成流。
    1. List<String> strings = Arrays.asList("H", "e", "l", "Hello", "HelloWorld", "o");
    2. Stream<String> stream = strings.stream();
    以上,通过一个已有的List创建一个流。除此以外,还有一个parallelStream方法,可以为集合创建一个并行流。
    这种通过集合创建出一个Stream的方式也是比较常用的一种方式。
    2、通过Stream创建流
    可以使用Stream类提供的方法,直接返回一个由指定元素组成的流。
    1. Stream<String> stream = Stream.of("h", "e", "l", "Hello", "w", "o");
    如以上代码,直接通过of方法,创建并返回一个Stream。

Stream中间操作

Java Stream用法 - 图1

map

map 方法用于映射每个元素到对应的结果

  1. // 转换大小写
  2. List<String> output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList());
  3. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  4. numbers.stream().map( i -> i*i).forEach(System.out::println);
  5. //9,4,4,9,49,9,25

flatmap

flatmap

将多个Stream连接成一个Stream,这时候不是用新值取代Stream的值,与map有所区别,这是重新生成一个Stream对象。

  1. List<String> words = new ArrayList<String>();
  2. words.add("hello");
  3. words.add("world");
  4. public static Stream<Character> characterStream(String s){
  5. List<Character> result = new ArrayList<>();
  6. for (char c : s.toCharArray())
  7. result.add(c);
  8. return result.stream();
  9. }
  10. Stream<Stream<Character>> result = words.map(w -> characterStream(w));
  11. Stream<Character> letters = words.flatMap(w -> characterStream(w));

如果使用的是map方法,返回的是[ [‘h’, ‘e’, ‘l’, ‘l’,’o’], [‘w’, ‘o’, ‘r’, ‘l’,’d’] 如果使用的是flatMap方法,返回的是[‘h’, ‘e’, ‘l’, ‘l’,’o’,’w’, ‘o’, ‘r’, ‘l’,’d’]

filter

filter 方法用于通过设置的条件过滤出元素。

  1. Integer[] sixNums = {1, 2, 3, 4, 5, 6};
  2. //只留偶数
  3. Integer[] evens = Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
  4. //使用 filter 方法过滤掉空字符串
  5. List<String> strings = Arrays.asList("Halo", "", "h", "H", "hello");
  6. strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);

limit/skip

limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。

  1. // 返回前四个元素
  2. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  3. numbers.stream().limit(4).forEach(System.out::println);
  4. //3,2,2,3

sorted

sorted 方法用于对流进行排序。

  1. Random random = new Random();
  2. // 对10个随机数进行排序输出
  3. random.ints().limit(10).sorted().forEach(System.out::println);

distinct

distinct主要用来去重,以下代码片段使用 distinct 对元素进行去重:

  1. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  2. numbers.stream().distinct().forEach(System.out::println);
  3. //3,2,7,5

Stream最终操作

Stream的中间操作得到的结果还是一个Stream,那么如何把一个Stream转换成我们需要的类型呢?比如计算出流中元素的个数、将流装换成集合等。这就需要最终操作(terminal operation)
最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能在使用任何中间操作,否则将抛出异常:

  1. java.lang.IllegalStateException: stream has already been operated upon or closed

Java Stream用法 - 图2

forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。

  1. Random random = new Random();
  2. // 随机输出10个整数
  3. random.ints().limit(10).forEach(System.out::println);

Coun

count用来统计流中的元素个数。

  1. List<String> strings = Arrays.asList("Hello", "World", "h","e", "l", "l", "o");
  2. System.out.println(strings.stream().count());
  3. //7

Collect

collect就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果:

  1. List<String>strings = Arrays.asList("you", "are", "stupid", "ha", "ha","", "a");
  2. List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
  3. System.out.println("筛选列表: " + filtered);
  4. String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
  5. System.out.println("合并字符串: " + mergedString);

下图包含了所有的操作 Java Stream用法 - 图3