一.什么是 Stream 流

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

  1. Stream(流)是一个来自数据源的元素队列并支持聚合操作
  • 元素:特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源:流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
  1. 和以前的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流

java 新版本特性 java新特性collect,stream_java 新版本特性