• 8.JDK8新特性Stream流


    一.什么是 Stream 流

    Java8开始,得益于Lambda所带来的函数式编程,引入一个全新的Stream概念,用于解决已有集合类库的弊端。

    1. Stream(流)是一个来自数据源的元素队列并支持聚合操作
      • 元素:特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
      • 数据源:流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
      • 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
    2. 和以前的Collection操作不同, Stream操作还有两个基础的特征:
      • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
      • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

    二.传统方式与Stream流对比

    1.传统方式:

     1 public class Demo01List {
     2 
     3     public static void main(String[] args) {
     4 
     5         List<String> list = new ArrayList<>();
     6 
     7         list.add("张无忌");
     8         list.add("周芷若");
     9         list.add("赵敏");
    10         list.add("张强");
    11         list.add("张三丰");
    12 
    13         //传统方式:找出姓张,姓名长度大于2的人,左后打印出来
    14         List<String> listA = new ArrayList<>();
    15         for (String s : list) {
    16             if (s.startsWith("张")) {
    17                 listA.add(s);
    18             }
    19         }
    20 
    21         List<String> listB = new ArrayList<>();
    22         for (String s : listA) {
    23             if (s.length() > 2) {
    24                 listB.add(s);
    25             }
    26         }
    27 
    28         for (String s : listB) {
    29             System.out.println(s);
    30         }
    31 
    32     }
    33 }

    结果:

    1 张无忌
    2 张三丰

    2.使用Stream流的方式:

     1 public class Demo01Stream {
     2 
     3     public static void main(String[] args) {
     4 
     5         List<String> list = new ArrayList<>();
     6 
     7         list.add("张无忌");
     8         list.add("周芷若");
     9         list.add("赵敏");
    10         list.add("张强");
    11         list.add("张三丰");
    12 
    13         //Stream流的方式:找出姓张,姓名长度大于2的人,左后打印出来
    14         list.stream()
    15                 .filter(name->name.startsWith("张"))
    16                 .filter(name->name.length()>2)
    17                 .forEach(name-> System.out.println(name));
    18 
    19     }
    20 }

    结果:

    1 张无忌
    2 张三丰

    结论:大大简化了代码

    三.获取Stream流的两种方式

    • 将集合转换为Stream流(只对单列集合):
      • default Stream<E> stream()
    • 将数组转换为Stream流:
      • static <T> Stream<T> of(T... values)
     1 public class Demo01GetStream {
     2 
     3     public static void main(String[] args) {
     4 
     5         //1.将集合转换为Stream流
     6         //List
     7         List<String> list = new ArrayList<>();
     8         Stream<String> stream1 = list.stream();
     9 
    10         //Set
    11         Set<String> set = new HashSet<>();
    12         Stream<String> stream2 = set.stream();
    13 
    14         //Map
    15         Map<String,String> map = new HashMap<>();
    16         //获取键转换为Stream流
    17         Set<String> keyset = map.keySet();
    18         Stream<String> stream3 = keyset.stream();
    19         //获取值转换为Stream流
    20         Collection<String> values = map.values();
    21         Stream<String> stream4 = values.stream();
    22         //获取键值对的映射关系entrySet转换为Stream流
    23         Set<Map.Entry<String,String>> entries = map.entrySet();
    24         Stream<Map.Entry<String,String>> stream5 = entries.stream();
    25 
    26         //2.将数组转换为Stream流
    27         //可变参数
    28         Stream<Integer> stream6 = Stream.of(1,2,3,4,5);
    29         //固定数组
    30         Integer[] arr = {1,2,3,4,5};
    31         Stream<Integer> stream7 = Stream.of(arr);
    32 
    33     }
    34 }

    四.Stream流中的常用方法

    常用方法分为两种类型:

    • 延迟方法:返回类型仍然是Stream接口自身类型的方法,支持链式调用(除了终结方法外,都是延迟方法)
    • 终结方法:返回类型不再是Stream接口自身类型的方法,因此不能链式调用了,中介方法包括count和forEach方法。

    1.终结方法:

    逐一处理:forEach

    • void forEach(consumer<? super T> action):该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。(终结方法)
     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("张无忌");
     7         list.add("周芷若");
     8         list.add("赵敏");
     9         list.add("张强");
    10         list.add("张三丰");
    11 
    12         list.stream().forEach((name)-> System.out.println(name));
    13 
    14         /**
    15          * 输出:
    16          * 张无忌
    17          * 周芷若
    18          * 赵敏
    19          * 张强
    20          * 张三丰
    21          */
    22     }
    23 }

    统计个数方法:count

    • long count():统计流中元素的个数。(终结方法)
     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("1");
     7         list.add("2");
     8         list.add("3");
     9         list.add("4");
    10         list.add("5");
    11 
    12         System.out.println(list.stream().count());
    13 
    14         /**
    15          * 输出:
    16          * 5
    17          */
    18     }
    19 }

    2.延迟方法:

    过滤:filter

    • Stream<T> filter(Predicate<? super T> predicate):可以通过filter方法将一个流转换成另一个子集流。

    • 注意:不会修改原来集合,而是会新生成一个集合
     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("张无忌");
     7         list.add("周芷若");
     8         list.add("赵敏");
     9         list.add("张强");
    10         list.add("张三丰");
    11 
    12         list.stream().filter((name) -> name.startsWith("张")).forEach((name) -> System.out.println(name));
    13 
    14         System.out.println("---------------");
    15         for (String s : list) {
    16             System.out.println(s);
    17         }
    18         /**
    19          * 输出:
    20          * 张无忌
    21          * 张强
    22          * 张三丰
    23          * ---------------
    24          * 张无忌
    25          * 周芷若
    26          * 赵敏
    27          * 张强
    28          * 张三丰
    29          */
    30     }
    31 }

    注意:

    Stream流特点:

    • Stream流属于管道流,只能被消费一次。
    • 前一个Stream流执行完毕,数据会传递给下一个流。这样前一个流就会关闭不能在对前一个流调用方法了。
    • 报错:java.lang.IllegalStateException: stream has already been operated upon or closed
     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("张无忌");
     7         list.add("周芷若");
     8         list.add("赵敏");
     9         list.add("张强");
    10         list.add("张三丰");
    11 
    12         Stream<String> stream1 = list.stream();
    13         Stream<String> stream2 = stream1.filter((name) -> name.startsWith("张"));
    14         stream2.forEach((name) -> System.out.println(name));
    15 
    16         //报错:java.lang.IllegalStateException: stream has already been operated upon or closed
    17         //报错原因是stream1已经被使用过了
    18         Stream<String> stream3 = stream1.filter((name) -> name.length() > 2);
    19         stream3.forEach((name) -> System.out.println(name));
    20 
    21         //报错原因stream2已经被使用过了
    22         stream2.forEach((name) -> System.out.println("--" + name));
    23     }
    24 }

    映射:map

    • <R> Stream<R> map(Function<? super T, ? extends R> mapper):将一个流映射到另一个流中(将类型T转换为类型R)

     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("1");
     7         list.add("2");
     8         list.add("3");
     9         list.add("4");
    10         list.add("5");
    11 
    12         list.stream().map((s) -> Integer.parseInt(s)).forEach((i) -> System.out.println(i));
    13 
    14         /**
    15          * 输出:
    16          * 1
    17          * 2
    18          * 3
    19          * 4
    20          * 5
    21          */
    22     }
    23 }

    取用前几个元素:limit

    • Stream<T> limit(long maxSize):对流进行截取,只取用前几个元素。

     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("1");
     7         list.add("2");
     8         list.add("3");
     9         list.add("4");
    10         list.add("5");
    11 
    12         list.stream().limit(3).forEach((s) -> System.out.println(s));
    13 
    14         /**
    15          * 输出:
    16          * 1
    17          * 2
    18          * 3
    19          */
    20     }
    21 }

    跳过前几个元素:skip

    • Stream<T> skip(long n):跳过流的前几个元素,剩下的元素生成新流。

     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("1");
     7         list.add("2");
     8         list.add("3");
     9         list.add("4");
    10         list.add("5");
    11 
    12         list.stream().skip(3).forEach((s) -> System.out.println(s));
    13 
    14         /**
    15          * 输出:
    16          * 4
    17          * 5
    18          */
    19     }
    20 }

    组合:concat

    • public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b):Stream类中的静态方法,将两个流合并为一个新的流使用。

     1 public class StreamTest {
     2 
     3     public static void main(String[] args) {
     4         List<String> list = new ArrayList<>();
     5 
     6         list.add("1");
     7         list.add("2");
     8         list.add("3");
     9         list.add("4");
    10         list.add("5");
    11 
    12         Stream<String> stream1 = list.stream().limit(2);
    13         Stream<String> stream2 = list.stream().skip(3);
    14 
    15         Stream.concat(stream1, stream2).forEach((s) -> System.out.println(s));
    16 
    17         /**
    18          * 输出:
    19          * 1
    20          * 2
    21          * 4
    22          * 5
    23          */
    24     }
    25 }

    补充:来自B站字母哥课堂

    调试Stream流

  • 相关阅读:
    转:算法的空间复杂度
    转:算法的最坏情况与平均情况 复杂度就要看最坏情况
    转:一些字符串函数的实现
    转:C语言字符串操作函数
    搜狐在线笔试 时间复杂度O(n)实现数组A[n]中所有元素循环左移k个位置
    搜狐笔试 最大连续递增子段和 关键词连续递增
    转:最小区间:k个有序的数组,找到最小区间使k个数组中每个数组至少有一个数在区间中
    转:strcpy实现的考察要点
    转:strcat与strcpy与strcmp与strlen
    转:多篇文章中的设计模式-------策略模式
  • 原文地址:https://www.cnblogs.com/zhihaospace/p/12148398.html
Copyright © 2020-2023  润新知