赞
踩
Java8中有两大最为重要的改变,其一是Lambda表达式,另外就是 Stream API了,本文就来简单学习一下Stream API(java.util.stream.*)。
Stream 是 Java8中处理集合的关键抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似使用SQL执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
在Stream操作过程中,可以对数据流做过滤,排序,切片等操作,但是操作之后会产生一个新的流,而数据源则不会发生改变。
一、什么是 Stream
Stream是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。而集合讲的是数据,流讲的是计算。
注意:
①. Stream 自己不会存储元素。
②. Stream 不会改变源对象。相反,它会返回一个持有结果的新Stream
③. Stream 操作是延迟执行的,这意味着它们会等到需要结果时才执行。(延迟加载)
二、Stream 操作的三个步骤
1). 创建 Stream
一个数据源(集合,数组),获取一个流。
2). 中间操作
一个中间操作链,对数据源的数据进行处理。
3). 终止操作
一个终止操作,执行中间操作链,并产生结果。
三、创建Stream 的四种方式
1). 通过Collection的Stream()方法(串行流)或者 parallelStream()方法(并行流)创建Stream。
- /**
- * 创建 Stream的四种方式
- * 1.通过Collection得Stream()方法(串行流)
- 或者 parallelStream()方法(并行流)创建Stream
- */
- @Test
- public void test1 () {
-
- //1. 通过Collection得Stream()方法(串行流)
- //或者 parallelStream()方法(并行流)创建Stream
- List<String> list = new ArrayList<String>();
- Stream<String> stream1 = list.stream();
-
- Stream<String> stream2 = list.parallelStream();
-
- }

2).通过Arrays中的静态方法stream()获取数组流
- /**
- * 创建 Stream的四种方式
- * 2. 通过Arrays中得静态方法stream()获取数组流
- */
- @Test
- public void test2 () {
-
- //2. 通过Arrays中得静态方法stream()获取数组流
- IntStream stream = Arrays.stream(new int[]{3,5});
-
- }
3). 通过Stream类中的 of()静态方法获取流
- /**
- * 创建 Stream的四种方式
- * 3. 通过Stream类中得 of()静态方法获取流
- */
- @Test
- public void test3 () {
-
- //3. 通过Stream类中得 of()静态方法获取流
- Stream<String> stream = Stream.of("4645", "huinnj");
-
- }
4). 创建无限流(迭代、生成)
- /**
- * 创建 Stream的四种方式
- * 4. 创建无限流(迭代、生成)
- */
- @Test
- public void test4 () {
-
- //4. 创建无限流
- //迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
- Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2);
-
- //生成(无限产生对象)
- Stream<Double> stream2 = Stream.generate(() -> Math.random());
-
- }
四、Stream 中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而终止操作时一次性全部处理,称为‘延迟加载’
1). 筛选与切片
①. filter —— 接收Lambda ,从流中排除某些元素。
- /**
- * 筛选与切片
- * filter —— 接收Lambda ,从流中排除某些元素。
- *
- */
- @Test
- public void test5 () {
- //内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代
- //中间操作:不会执行任何操作
- Stream<Person> stream = list.stream().filter((e) -> {
- System.out.println("Stream API 中间操作");
- return e.getAge() > 30;
- });
-
- //终止操作:只有执行终止操作才会执行全部。即:延迟加载
- stream.forEach(System.out :: println);
-
- }

执行上面方法,得到下面结果。
Person [name=张三, sex=男, age=
76
]
Stream API 中间操作
Stream API 中间操作
Person [name=王五, sex=男, age=
35
]
Stream API 中间操作
Stream API 中间操作
Person [name=钱七, sex=男, age=
56
]
Stream API 中间操作
Person [name=翠花, sex=女, age=
34
]
我们在执行终止语句之后,一边迭代,一边打印,而我们并没有去迭代上面的集合,其实这是内部迭代,由Stream API 完成。
下面我们来看看外部迭代,也就是我们人为的迭代。
- @Test
- public void test6 () {
- //外部迭代
- Iterator<Person> it = list.iterator();
- while (it.hasNext()) {
- System.out.println(it.next());
- }
-
- }
②. limit —— 截断流,使其元素不超过给定数量。
- /**
- * limit —— 截断流,使其元素不超过给定数量。
- */
- @Test
- public void test7 () {
- //过滤之后取2个值
- list.stream().filter((e) -> e.getAge() >30 ).
- limit(2).forEach(System.out :: println);
-
- }
在这里,我们可以配合其他的中间操作,并截断流,使我们可以取得相应个数的元素。而且在上面计算中,只要发现有2条符合条件的元素,就不会继续往下迭代数据,可以提高效率。
2). 跳过元素
skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。
- /**
- * skip(n)—— 跳过元素,返回一个扔掉了前n个元素的流。
- * 若流中元素不足n个,则返回一个空,与limit(n)互补。
- */
- @Test
- public void test8 () {
- //跳过前2个值
- list.stream().skip(2).forEach(System.out :: println);
-
- }
3). 筛选
distinct 通过流所生成元素的hashCode()和equals()去除重复元素
- /**
- * distinct —— 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
- */
- @Test
- public void test9 () {
-
- list.stream().distinct().forEach(System.out :: println);
-
- }
注意:distinct 需要实体中重写hashCode()和 equals()方法才可以使用
4). 映射
① . map ,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- /**
- * map —— 映射 ,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- */
- @Test
- public void test10 () {
- //将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
- List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
- list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
-
- //获取Person中的每一个人得名字name,再返回一个集合
- List<String> names = this.list.stream().map(Person :: getName).
- collect(Collectors.toList());
- }
② . flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
- /**
- * flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
- */
- @Test
- public void test11 () {
- StreamAPI_Test s = new StreamAPI_Test();
- List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
- list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
-
- //如果使用map则需要这样写
- list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
- e.forEach(System.out::println);
- });
- }
-
- /**
- * 将一个字符串转换为流
- * @param str
- * @return
- */
- public Stream<Character> filterCharacter(String str){
- List<Character> list = new ArrayList<>();
- for (Character ch : str.toCharArray()) {
- list.add(ch);
- }
- return list.stream();
- }

其实map方法就相当于Collaction的add方法,如果add的是个集合的话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合的话,只是将2个集合合并,而不是变成二维数组。
5). 排序
sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。
- /**
- * sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。
- */
- @Test
- public void test12 () {
- // 自然排序
- List<Person> persons = list.stream().sorted().collect(Collectors.toList());
-
- //定制排序
- List<Person> persons1 = list.stream().sorted((e1, e2) -> {
- if (e1.getAge() == e2.getAge()) {
- return 0;
- } else if (e1.getAge() > e2.getAge()) {
- return 1;
- } else {
- return -1;
- }
- }).collect(Collectors.toList());
- }

五、Stream 终止操作
1). 查找与匹配
首先我们先创建一个集合。
- List<Person> persons = Arrays.asList(
- new Person("张三", "男", 76, Status.FREE),
- new Person("李四", "女", 12, Status.BUSY),
- new Person("王五", "男", 35, Status.BUSY),
- new Person("赵六", "男", 3, Status.FREE),
- new Person("钱七", "男", 56, Status.BUSY),
- new Person("翠花", "女", 34, Status.VOCATION),
- new Person("翠花", "女", 34, Status.FREE),
- new Person("翠花", "女", 34, Status.VOCATION)
- );
①. allMatch —— 检查是否匹配所有元素。
- /**
- * allMatch —— 检查是否匹配所有元素。
- * 判断所有状态是否都是FREE
- */
- @Test
- public void test13 () {
- boolean b = persons.stream().allMatch((e) -> Status.FREE.equals(e.getStatus()));
- System.out.println(b);
- }
②. anyMatch —— 检查是否至少匹配所有元素。
- /**
- * anyMatch —— 检查是否至少匹配所有元素。
- * 判断是否有一个是FREE
- */
- @Test
- public void test14 () {
- boolean b = persons.stream().anyMatch((e) -> Status.FREE.equals(e.getStatus()));
- System.out.println(b);
- }
③. noneMatch —— 检查是否没有匹配所有元素。
- /**
- * noneMatch —— 检查是否没有匹配所有元素。
- * 判断是否没有FREE
- */
- @Test
- public void test15 () {
- boolean b = persons.stream().noneMatch((e) -> Status.FREE.equals(e.getStatus()));
- System.out.println(b);
- }
④. findFirst —— 返回第一个元素。
- /**
- * findFirst —— 返回第一个元素。
- *
- */
- @Test
- public void test16 () {
- Optional<Person> person = persons.stream().findFirst();
- System.out.println(person);
-
- person.orElse(new Person("王五", "男", 35, Status.BUSY));
- }
注意:上面findFirst 返回的是一个Optional的对像,他将我们的Person封装了一层,这是为了避免空指针。而且这个对象为我们提供了一个orElse方法,就是当我们得到的这个对象为空时,我们可以传入一个新得对象去替代它。
⑤. findAny —— 返回当前流中任意元素。
- /**
- * findAny —— 返回当前流中任意元素。
- */
- @Test
- public void test17 () {
- Optional<Person> person = persons.stream().findAny();
- System.out.println(person);
-
- person.orElse(new Person("王五", "男", 35, Status.BUSY));
- }
⑥. count —— 返回流中元素总个数。
- /**
- * count —— 返回流中元素总个数。
- */
- @Test
- public void test18 () {
- long count = persons.stream().count();
- System.out.println(count);
-
- }
⑦. max —— 返回流中最大值。
- /**
- * max —— 返回流中最大值。
- */
- @Test
- public void test18 () {
- Optional<Person> person = persons.stream().max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
- System.out.println(person);
-
- }
⑧. min —— 返回流中最小值。
- /**
- * min —— 返回流中最小值。
- */
- @Test
- public void test20 () {
- Optional<Person> person = persons.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
- System.out.println(person);
-
- }
2). 归约(可以将流中元素反复结合在一起,得到一个值)
①. reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。
- /**
- * reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。
- */
- @Test
- public void test21 () {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Integer sum = list.stream().reduce(0, (x, y) -> x + y);
- }
②. reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中。
- /**
- * reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中
- */
- @Test
- public void test22 () {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Optional<Integer> sum = list.stream().reduce(Integer :: sum);
- }
备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。
3). 收集collect(将流转换为其他形式。接收一个Collector接口得实现,用于给其他Stream中元素做汇总的方法)
Collector接口中方法得实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见得收集器实例。
①. Collectors.toList() 将流转换成List
- /**
- * Collectors.toList() 将流转换成List
- */
- @Test
- public void test23() {
- List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
- }
②. Collectors.toSet()将流转换为Set
- /**
- * Collectors.toSet() 将流转换成Set
- */
- @Test
- public void test24() {
- Set<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toSet());
- }
③. Collectors.toCollection()将流转换为其他类型的集合
- /**
- * Collectors.toCollection()将流转换为其他类型的集合
- */
- @Test
- public void test25() {
- LinkedList<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toCollection(LinkedList :: new));
- }
④. Collectors.counting() 元素个数
- /**
- * Collectors.counting() 总数
- */
- @Test
- public void test26() {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Long count = list.stream().collect(Collectors.counting());
- }
⑤. Collectors.averagingDouble()、Collectors.averagingDouble()、Collectors.averagingLong() 平均数,这三个方法都可以求平均数,不同之处在于传入得参数类型不同,返回值都为Double
- /**
- * Collectors.averagingInt() 、
- * Collectors.averagingDouble()、
- * Collectors.averagingLong() 平均数,
- * 者三个方法都可以求平均数,不同之处在于传入得参数类型不同,
- * 返回值都为Double
- */
- @Test
- public void test27() {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Double avg = list.stream().collect(Collectors.averagingInt((x) -> x));
- }
⑥. Collectors.summingDouble()、Collectors.summingDouble()、Collectors.summingLong() 求和,不同之处在于传入得参数类型不同,返回值为Integer, Double, Long
- /**
- * Collectors.summingInt() 、
- * Collectors.summingDouble()、
- * Collectors.summingLong() 求和,
- * 者三个方法都可以求总数,不同之处在于传入得参数类型不同,
- * 返回值为Integer, Double, Long
- */
- @Test
- public void test28() {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Integer sum = list.stream().collect(Collectors.summingInt((x) -> x));
- }
⑦. Collectors.maxBy() 求最大值
- /**
- * Collectors.maxBy() 求最大值
- */
- @Test
- public void test29() {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Optional<Integer> max = list.stream().collect(Collectors.maxBy((x, y) ->Integer.compare(x, y)));
- }
⑧. Collectors.minBy() 求最小值
- /**
- * Collectors.minBy() 求最小值
- */
- @Test
- public void test29() {
- List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
- Optional<Integer> min = list.stream().collect(Collectors.minBy((x, y) ->Integer.compare(x, y)));
- }
⑨. Collectors.groupingBy()分组 ,返回一个map
- /**
- * Collectors.groupingBy()分组 ,返回一个map
- */
- @Test
- public void test30() {
- Map<String, List<Person>> personMap = list.stream().collect(Collectors.groupingBy(Person :: getSex));
- }
Collectors.groupingBy()还可以实现多级分组
- /**
- * Collectors.groupingBy()多级分组 ,返回一个map
- */
- @Test
- public void test31() {
- Map<String, Map<Status, List<Person>>> personMap = list.stream().collect(Collectors.groupingBy(Person :: getSex, Collectors.groupingBy(Person :: getStatus)));
- }
⑩. Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区
- /**
- * Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区
- */
- @Test
- public void test32() {
- Map<Boolean, List<Person>> personMap = list.stream().collect(Collectors.partitioningBy((x) -> x.getAge() > 30));
- }
上面就是Stream的一些基本操作,只要勤加练习就可以灵活使用,而且效率大大提高。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。