JDK8新特性-StreamAPI

JDK8新特性-StreamAPI

Java 8 Stream | 菜鸟教程 https://www.runoob.com/java/java8-streams.html

菜鸟教程的目录是真的清晰,再结合优秀博主的测试用例,(和更详细的测试用例)完成了以下的学习文档。

也相当于一个工具手册了,方便查阅。

一、说明

1.0 发展历史

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

1.1 什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

1.2 整体流程

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds = 
widgets.stream()
            .filter(b -> b.getColor() == RED)
            .sorted((x,y) -> x.getWeight() - y.getWeight())
            .mapToInt(Widget::getWeight)
            .sum();

下面就按照这个流程来详解每个环节都有哪些语法。

二、“生命周期”

2.1 生成流

在 Java 8 中, 集合接口有两个方法来生成流:

stream() − 为集合创建串行流

parallelStream() − 为集合创建并行流

根据大佬的说法:

使用parallelStream就能立即获得一个拥有并行能力的流,利用好并行化非常重要。不过并不是所有的流计算都要使用并行化流操作,只有当计算机是多核并且集合数据量较大(超过一万)的时候使用并行化流操作可以提高效率。

影响性能的五要素:数据大小、源数据结构、值是否装箱、可用的CPU数量以及处理每个元素所花的时间。

2.2 迭代流 forEach

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

ArrayList<String> list = CollectionUtil.newArrayList("aaa", "bbb", "ccc", "ddd", "eee", "   ");
list.forEach(System.out::println);

2.3 元素映射 map

map 方法用于映射每个元素到对应的结果。注意:是“每个”。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 4, 3, 2, 1);
List<Integer> collect = list.stream().map(i -> i * i).collect(Collectors.toList());
//转JSONStr输出结果:[1,4,9,16,25,16,9,4,1]

我们看到map家族下面有很多转int、double、long的,其目的是返回数值流,减少拆箱封箱操作,避免占用内存

image-20210629161943892

2.4 过滤元素 filter

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

以下代码片段使用 filter 方法过滤出空字符串:

List<String>strings = Arrays.asList("abc", "", "bc", "efg"````````````````````````````````````	, "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

2.4.1 存在性判断 anyMatch

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
boolean b = integers.stream().anyMatch(p -> p > 10);
//打印结果:false

2.5 部分取 limit

limit 方法用于获取指定数量的流。

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
List<Integer> collect = integers.stream().limit(3).collect(Collectors.toList());
//结果:[1,2,3],说明是按照指定顺序来取对行的数量,不要和index混淆了。

2.6 流排序 sorted

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

//默认情况下:
List<Integer> integers = Arrays.asList(7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6);
integers.stream().sorted().forEach(System.out::println);

自定义排序条件(扩展内容比较大范围,待考究):

//按照年龄从小到大排序
List<Student> list = list.stream().sorted((s1, s2) -> s1.getAge().compareTo(s2.getAge())).collect(toList());

2.7 归约操作 Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。

以下测试用例有用到hutool工具包

2.7.1 基本返回 toList、toSet、toCollection

在collect之前做的赛选或者转值的话,在collect里面直接返回结果集:

JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);
//返回结果的对象类型就是本身
Stream<JSONObject> age = list.stream().filter(s -> s.getInt("age") > 20);
//返回map()之后的对象类型
List<Integer> integers = list.stream().map(p -> p.getInt("age")).collect(Collectors.toList());
List<String> name = list.stream().map(p -> p.getStr("name")).collect(Collectors.toList());

2.7.2 分而治之 partitioningBy(二分法)

很骚的一个方法,按照某个条件,把所有的数据分成2类,条件结果为true的归一类,false的归一类,同属一个map里面:

JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3);
//分而治之
Map<Boolean, List<JSONObject>> map = list.stream().collect(Collectors.partitioningBy(s -> s.getInt("age") > 20));
//输出结果:
{
    "false":[{"gender":"男","name":"张三","age":18},{"gender":"男","name":"李四","age":19}],
    "true":[{"gender":"女","name":"王五","age":22}]
}

2.7.3 分组 groupingBy

类似数据库的分组,指定的条件作为分组的依据以及结果集的key:

JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);

//分组,指定key但不指定value的处理:
Map<Integer, List<JSONObject>> groupingByAge = list.stream().collect(Collectors.groupingBy(s -> s.getInt("age")));
//打印结果:
{"22":[{"gender":"女","name":"王五","age":22},{"gender":"女","name":"王五","age":22}],"18":[{"gender":"男","name":"张三","age":18}],"19":[{"gender":"男","name":"李四","age":19}]}

//分组,指定key,指定value的处理:
//(1)计数
Map<Integer, Long> m1 = list.stream().collect(Collectors.groupingBy(s -> s.getInt("age"), Collectors.counting()));
//打印结果:
{"22":2,"18":1,"19":1}

//(2)求和
Map<Integer, Integer> m = list.stream().collect(Collectors.groupingBy(s -> s.getInt("age"), Collectors.summingInt(p -> 3.6)));
//打印:{"22":6,"18":3,"19":3}

2.7.4 计数counting()

JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);
//筛选后计数
Long collect = list.stream().filter(p -> p.getInt("age") > 20).collect(Collectors.counting());
//结果:2

2.7.5 最小值minBy()

这里的最小值有传统意义上的数组最新,也可以指代排序里面的首位,比如a-z中的第一位是a:

List<Double> doubles = Arrays.asList(6.1, 5.1, 7.8, 99.0, 6.8);
List<String> strings = Arrays.asList("aba", "aaa", "ble", "cas");
Double minNum = doubles.stream().collect(Collectors.minBy(Comparator.naturalOrder())).get();//结果:5.1
String minStr = strings.stream().collect(Collectors.minBy(Comparator.naturalOrder())).get();//结果:“aaa”
//如果是对象,需要指定比较的属性
JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);
list.stream().collect(Collectors.minBy(Comparator.comparing(s -> s.getInt("age")))).get();

2.7.6 最大值maxBy()

参考minBy()

2.7.7 连接元素 joining()

参数含义分别是:分隔符,左包含符号、右包含符号:

List<Integer> integers = Arrays.asList(6, 5, 7, 8, 321, 5, 8, 123, 10);
String strBuffer = integers.stream().map(p -> p + "").collect(Collectors.joining(","));//不指定前后
//打印结果:6,5,7,8,321,5,8,123,10
String strBuffer2 = integers.stream().map(p -> p + "").collect(Collectors.joining(",", "《", "》"));//指定前后
//打印结果:《6,5,7,8,321,5,8,123,10》

2.7.8 求平均值

long、integer、double分别求平均值(注意返回值都是double):

//(1)integer类型
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Double collect = integers.stream().collect(Collectors.averagingInt(x -> x));//x->x只不需要对x进行其他操作;
//如果是包含在对象里面的话
JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);

Double aver = list.stream().collect(Collectors.averagingInt(p -> p.getInt("age")));//对象里面取出int

//(2)long类型
List<Long> longs = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L);
Double aves = longs.stream().collect(Collectors.averagingLong(p -> p));

//(3)double类型
List<Double> doubles = Arrays.asList(1D, 2D, 3D, 4D, 5D, 6D, 7D, 8D, 9D);
Double aves3 = doubles.stream().collect(Collectors.averagingDouble(h -> h));

2.7.10 求和

同样有integer、long、double类型的求和

//数值类型本身进行求和
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer sumInt = integers.stream().collect(Collectors.summingInt(p -> p));//(p -> p)指不对元素数据做任何操作

//非数值类型
List<String> strings = Arrays.asList("alpha", "beta", "gamma", "beta");
strings.stream().collect(Collectors.summingInt(String::length));
strings.stream().collect(Collectors.summingInt(p -> p.length()));

//entity类型
JSONObject student1 = new JSONObject().putOpt("name", "张三").putOpt("gender", "男").putOpt("age", 18);
JSONObject student2 = new JSONObject().putOpt("name", "李四").putOpt("gender", "男").putOpt("age", 19);
JSONObject student3 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
JSONObject student4 = new JSONObject().putOpt("name", "王五").putOpt("gender", "女").putOpt("age", 22);
ArrayList<JSONObject> list = CollectionUtil.newArrayList(student1, student2, student3, student4);
Integer sumAges = list.stream().collect(Collectors.summingInt(p -> p.getInt("age")));


//----------------------------------
//Long类型
List<Long> longs = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L);
longs.stream().collect(Collectors.summingLong(p -> p));
//double类型
List<Double> doubles = Arrays.asList(1D, 2D, 3D, 4D, 5D, 6D, 7D, 8D, 9D);
doubles.stream().collect(Collectors.summingDouble(p -> p));

2.7.11 运算结果汇总summarizingXXX

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
IntSummaryStatistics stats = integers.stream().collect(Collectors.summarizingInt(x -> x));
//stats:{"max":9,"count":9,"sum":45,"min":1},可以通过get方法获取
stats.getMax();
stats.getMin();
stats.getCount();
stats.getSum();
stats.getAverage();

//long和double类型
longs.stream().collect(Collectors.summarizingLong(l -> l));
doubles.stream().collect(Collectors.summingDouble(l -> l));

2.7.12 创建Map集合 toMap()

//(1)根据集合的值创建Map
List<String> strings = Arrays.asList("alpha", "beta", "gamma");
Map<String, Integer> map = strings.stream().collect(Collectors.toMap(Function.identity(), String::length));
//打印结果:{"alpha":5,"gamma":5,"beta":4}

//(2)在创建map时处理列表的重复项
//数据源集合中可以包含重复的值,因此,如果想从列表中创建一个Map,并希望使用集合值作为map的key,那么需要解析重复的key。由于map只包含唯一的key,可以使用比较器来实现这一点。
List<String> strings = Arrays.asList("alpha", "beta", "gamma", "beta");
Map<String, Integer> map = strings.stream().collect(Collectors.toMap(Function.identity(), String::length, (i1, i2) -> i1));
// output: {alpha=5, gamma=5, beta=4}
//Function.identity()指向集合中的key值,String::length指代value值,(i1, i2) -> i1)指代保留策略,i1和i2是重复键的值。可以只保留一个值,这里选择i1,也可以用这两个值来计算任何东西,比如把它们相加,比较和选择较大的那个,等等。

power by dororo 2021-6-30 13:37:22

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值