Java8新特性——Stream API


前言

Stream是Java8的一大亮点,是对容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的 聚合操作(aggregate operation)或者大批量数据操作!

Stream API

1.概述

 Java 8扩展了集合类,可以通过 Collection.stream()或者 Collection.parallelStream()来创建一个Stream。
 Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者 SetMap不支持。Stream 的操作可以串行执行或者并行执行。

2.Stream的特点

  • Stream不是什么数据结构,它不会保存数据,只是将操作的数据结果保存到另一个对象中。
  • Stream是惰性求值的(延迟执行),在中间处理的过程中,只对操作进行记录,并不会立即执行,只有等到执行终止操作的时候才会进行实际的计算,这时候中间操作才会执行。
  • 可以把Stream当成一个高级版本的Iterator来使用。(原始版本的Iterator,只能一个个的遍历操作)

3.常用方法

Stream操作分类作用描述常用方法
无状态中间操作, 该操作不受之前元素的影响unordered、filter、map、mapToInt、mapToLong、mapToDouble、flatMap、flatMapToInt、flatMapToLongg、flatMapToDouble、peek
有状态中间操作,该操作只有拿到所有元素之后才能继续执行distinct、sorted、limit、skip
非短路操作结束操作 , 必须处理所有元素才能得到最终结果forEach、forEachOrdered、toArray、collect、max、min、count、reduce
短路操作结束操作 , 遇到某些符合条件的元素就可以得到最终结果anyMatch、allMatch、noneMatch、findFirsh、findAny

代码如下(示例):

初始化集合

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User1 {
    private String name;
}

List<String> stringList = Arrays.asList("a","b", "", "", "c", "", "a", "d","e","a");
System.out.println("字符串集合:" + stringList);

List<User1> userList = Arrays.asList(new User1("张三"),new User1("李四"),new User1("王五"));
System.out.println("用户集合:" + userList);

//输出:
字符串集合:[a, b, , , c, , a, d, e, a]
用户集合:[User1(name=张三), User1(name=李四), User1(name=王五)]

forEach

forEach()方法用于迭代遍历每个数据

代码如下(示例):

//遍历集合
stringList.forEach(str -> System.out.print(str + " "));
stringList.forEach(System.out::print); 

//输出:
a b   c  a d e a a
abcadea

filter

filter()方法用于按照设置的条件过滤元素,得到满足条件的元素

代码如下(示例):

//获取stringList中非空字符串的集合
List<String> collect = stringList.stream().filter(str -> !str.isEmpty())
        .collect(Collectors.toList());
System.out.println("非空字符串集合:" + collect);

//输出:非空字符串集合:[a, b, c, a, d, e, a]

count

count()方法用于统计数量

代码如下(示例):

//统计空字符串个数
long emptyStrNum = stringList.stream().filter(str -> str.isEmpty()).count();
System.out.println("空字符串数量 = " + emptyStrNum); 

//输出:空字符串数量 = 3

distinct

distinct()方法用于去重

代码如下(示例):

//获取stringList中非空字符串的集合 去重后 转化为list集合
List<String> collect3 = stringList.stream().filter(str -> !str.isEmpty()).distinct()
        .collect(Collectors.toList());
System.out.println("去重后字符串集合:" + collect3);

//提取出userList对象中的属性name并去重
List<String> nameList = userList.stream().map(User1::getName).distinct().collect(Collectors.toList());
System.out.println("nameList = " + nameList);

//输出:
去重后字符串集合:[a, b, c, d, e]
nameList = [张三, 李四, 王五]

Collectors - (Collector工具库)

 Collectors 类中实现了很多的规约操作(可用于返回列表或字符串)

Collectors.toList()

Collectors.toList()方法将Stream转化为List对象

代码如下(示例):

//查找非空、去重后通过 Collectors.toList() 转化为List列表
List<String> strList = stringList.stream().filter(str -> !str.isEmpty())
        .distinct().collect(Collectors.toList());
System.out.println("strList = " + strList);

//输出:strList = [a, b, c, d, e]
Collectors.toSet()

Collectors.toSet()方法将Stream转化为Set对象

代码如下(示例):

//查找非空后通过 Collectors.toSet() 转化为Set列表
Set<String> setList = stringList.stream().filter(str -> !str.isEmpty())
        .collect(Collectors.toSet());
System.out.println("setList = " + setList);

//输出:setList = [a, b, c, d, e]
Collectors.toMap()

Collectors.toMap()方法将Stream转化为Map对象

代码如下(示例):

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,BinaryOperator mergeFunction,Supplier mapSupplier)

Collectors.toMap()方法的四个参数:

  • 参数1: keyMapper 用来生成key值的。
  • 参数2: valueMapper 用来生成value值的。
  • 参数3: mergeFunction 用在key值冲突的情况下使用(可省略),如果新元素产生的key在Map中已经出现过了,第三个参数就会定义解决的办法。
  • 参数4:mapSupplier 默认返回的map类型为hashMap,可以按自己的需要自己返回不同的map实现。( 可省略 )
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User2 {
    private String name;
    private String age;
}

List<User2> userList1 = Arrays.asList(new User2("张三","18"),new User2("李四","19"),new User2("王五","20"));

//Collectors.toMap()方法,将List转化为Map集合
Map<String, String> map1 = userList1.stream().collect(Collectors.toMap(item -> item.getName(), item -> item.getAge()));
//map1的简写
Map<String, String> map2 = userList1.stream().collect(Collectors.toMap(User2::getName, User2::getAge));
Map<String, User2> map3 = userList1.stream().collect(Collectors.toMap(User2::getName, i -> i));
System.out.println(map1);
System.out.println(map2);
System.out.println(map3);

//输出:
{李四=19, 张三=18, 王五=20}
{李四=19, 张三=18, 王五=20}
{李四=User2(name=李四, age=19), 张三=User2(name=张三, age=18), 王五=User2(name=王五, age=20)}

注意: 如果map中出现相同的key,但是没有设置第三个参数对其进行处理,就会报错 IllegalStateException

代码如下(示例):创建一个对象集合存在相同的名字

List<User2> userList2 = Arrays.asList(new User2("张三","18"),new User2("李四","19"),new User2("张三","20"));
Map<String, String> map4 = userList2.stream().collect(Collectors.toMap(User2::getName, User2::getAge));
System.out.println(map4);

//输出:Exception in thread "main" java.lang.IllegalStateException: Duplicate key 18

解决报错:设置第三个参数,解决出现相同key时,谁覆盖谁的问题

//设置当key相同的时候,保留前面的 【如果为 (v1,v2)->v2 就是用新的覆盖旧的】
Map<String, String> map4 = userList2.stream().collect(Collectors.toMap(User2::getName, User2::getAge, (v1,v2) -> v1));
System.out.println(map4);

//输出:{李四=19, 张三=18}
统计
  • 获取集合数量:counting

代码如下(示例):

//获取集合StringList的元素数量
//Collectors.counting()
System.out.println(stringList.stream().collect(Collectors.counting()));
//简写为count()
System.out.println(stringList.stream().count());
//等价于集合的size方法
System.out.println(stringList.size());

//输出:
10
10
10
  • 求平均值:averagingIntaveragingLongaveragingDouble
  • 求最大/最小值:maxByminBy
  • 统计求和:summingIntsummingLongsummingDouble
  • 统计所有(包括计数、求和、最小、最大、平均):summarizingIntsummarizingLongsummarizingDouble

代码如下(示例):

List<Integer> intList = Arrays.asList(1, 3, 10, 6, 8, 5, 2, 9);
// maxBy()求最大值  --  等价于max
Optional<Integer> maxBy = intList.stream().collect(Collectors.maxBy(Integer::compareTo));
// minBy()求最大值  --  等价于min
Optional<Integer> minBy = intList.stream().collect(Collectors.minBy(Integer::compareTo));
// averagingInt()求平均值
Double averagingInt = intList.stream().collect(Collectors.averagingInt(i -> i));
// summingInt()求和 -- 等价于mapToInt
Integer summingInt = intList.stream().collect(Collectors.summingInt(i -> i));
// summarizingInt()统计数目、求和、最小值、平均值、最大值
IntSummaryStatistics summarizingInt = intList.stream().collect(Collectors.summarizingInt(i -> i));

System.out.println("maxBy = " + maxBy);
System.out.println("minBy = " + minBy);
System.out.println("averagingInt = " + averagingInt);
System.out.println("summingInt = " + summingInt);
System.out.println("summarizingInt = " + summarizingInt);

//输出:
maxBy = Optional[10]
minBy = Optional[1]
averagingInt = 5.5
summingInt = 44
summarizingInt = IntSummaryStatistics{count=8, sum=44, min=1, average=5.500000, max=10}
分组
  • partitioningBy(分区):按照条件分为两个 Map<Boolean, List>,一个是满足条件的Map和一个不满足条件的Map。
  • groupingBy(分组):类似于分区,但是是将集合按照条件分为多个Map,可以对进行分组之后的结果再分组。

代码如下(示例):

List<Integer> intList = Arrays.asList(1, 3, 10, 6, 8, 5, 2, 9);
//按 >5 分为两个区间
Map<Boolean, List<Integer>> partitioningBy = intList.stream().collect(Collectors.partitioningBy(i -> i > 5));
//按 >5 分为两组
Map<Boolean, List<Integer>> groupingBy = intList.stream().collect(Collectors.groupingBy(i -> i > 5));
//先按 >5 分为两组,然后再在前面分组满足条件的基础上对(满足条件的集合)再对 >8 进行分组
Map<Boolean, Map<Boolean, List<Integer>>> groupingBy2 = intList.stream().collect(Collectors.groupingBy(i -> i > 5, Collectors.groupingBy(i -> i > 8)));
System.out.println("分区 = " + partitioningBy);
System.out.println("分组 = " + groupingBy);
System.out.println("两次分组 = " + groupingBy2);

//输出:
分区 = {false=[1, 3, 5, 2], true=[10, 6, 8, 9]}
分组 = {false=[1, 3, 5, 2], true=[10, 6, 8, 9]}
两次分组 = {false={false=[1, 3, 5, 2]}, true={false=[6, 8], true=[10, 9]}}
joining

joining():可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个新的字符串。

代码如下(示例):

List<String> stringList2 = Arrays.asList("a","b", " ", " ", "c", " ", "a", "d","e","a");
String joining = stringList2.stream().collect(Collectors.joining("-"));
System.out.println("joining = " + joining);

//输出:joining = a-b- - -c- -a-d-e-a
reducing

reducing()方法利用Comparator(比较器)和BinaryOperator(二元运算符)进行减少流中的元素。规约

reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)

参数: BinaryOperator: 这是一个函数式接口,是给两个相同类型的量,返回一个跟这两个量相同类型的一个结果,伪表达式为 (T,T) -> T。默认给了两个实现 maxByminBy ,根据比较器来比较大小并分别返回最大值或者最小值。

使用场景: Collectors.reducing 主要用于取List列表中的某一类型中的最大或最小的一个元素。

注意: 如果最值相同,取列表顺序最先出现的一个。

代码如下(示例):

List<Integer> intList2 = Arrays.asList(1, 2, 3, 4);
//根据比较器来比较大小并分别返回最大值或者最小值
Integer reducing = intList2.stream().collect(Collectors.reducing(100, i -> i, (v1, v2) -> (v1 + v2 - 1))); // 100 + 10 - 4
System.out.println("reducing = " + reducing);
Integer reducing1 = intList2.stream().collect(Collectors.reducing(100, i -> i, (v1, v2) -> (v1 + v2 + 1)));// 100 + 10 + 4
System.out.println("reducing1 = " + reducing1);
Integer reducing2 = intList2.stream().collect(Collectors.reducing(100, i -> i, (v1, v2) -> (v1 + v2)));    // 100 + 10
System.out.println("reducing2 = " + reducing2);

Optional<Integer> reduce1 = Optional.ofNullable(intList2.stream().reduce(100, Integer::sum));
System.out.println("reduce1 " + reduce1.get());
Optional<Integer> reduce2 = Optional.ofNullable(intList2.stream().reduce(100, Integer::sum, (v1, v2) -> (v1 + v2 - 1)));
System.out.println("reduce2 = " + reduce2.get());

//输出:
reducing = 102
reducing1 = 114
reducing2 = 110
reduce1 110
reduce2 = 110
map

map()方法用于映射每个元素到对应的结果,该函数会被应用到每个元素上,并将其映射成一个新的元素。

代码如下(示例):

//使用map输出stringList中元素对应的两倍并去重
List<String> doubleStr = stringList.stream().map(str -> str += str).distinct()
        .collect(Collectors.toList());
System.out.print(doubleStr + " ");
//通过map获取userList列表中的User对象的name属性组成一个list列表
List<String> nameList1 = userList.stream().map(user -> user.getName()).distinct()
        .collect(Collectors.toList());
System.out.println(nameList1);

//输出:[aa, bb, , cc, dd, ee] [张三, 李四, 王五]
flatMap

flatMap()方法用于 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

代码如下(示例):

//flagMap转换为流后再转化为list列表输出
List<String> stringList1 = stringList.stream().flatMap(s -> {
    //将每个元素按照分隔符,转换成一个stream
    String[] split = s.split(",");
    return Arrays.stream(split);
}).collect(Collectors.toList());
System.out.println(stringList1);

//输出:[a, b, , , c, , a, d, e, a]
limit

limit(n) 方法用于获取指定数量 (n个) 的流。

代码如下(示例):

Random random = new Random();
//通过 limit 输出3个随机数
random.ints().limit(3).forEach(r -> System.out.print(r + " "));

System.out.println();
// 三个参数:random.ints(生成数量,最小值,最大值)
//生成10个1-100之间的随机数,再通过limit取出5个输出
random.ints(10,1,100).limit(5).forEach(r -> System.out.print(r + " "));
System.out.println();

//输出:
1667244870 1205442857 1105903851 
14 75 24 55 2 
skip

skip(n) 方法用于跳过 (n个) 元素,配合 limit(n) 可实现分页。

代码如下(示例):

List<Integer> numbers = Arrays.asList(0, 9 ,6 , 5, 6, 3, 2, 1, 12, 5, 8, 3, 9, 3);
//过滤得到集合中>1的元素,然后去重,跳过前2个,然后取出5个
List<Integer> integerList = numbers.stream().filter(i -> i > 1)
        .distinct()
        .skip(2)
        .limit(5).collect(Collectors.toList());
System.out.println(integerList);

//输出:[5, 3, 2, 12, 8]
sorted

sorted() 方法用于对流进行排序,这是自然排序,流中元素需实现Comparable接口。

代码如下(示例):

//两个参数:random.ints(最小值,最大值)
//随机生成0-1000的随机数,通过limit取8个,然后排序输出
random.ints(0,1000).limit(8).sorted().forEach(i -> System.out.print(i + " "));
System.out.println();

//输出:157 392 588 590 816 819 829 883 
统计结果收集器
  • 获取元素数量:getCount
  • 最大值/最小值:getMax/getMin
  • 总和:getSum
  • 平均值:getAverage

用于统计结果的收集器,主要用于int、double、long等基本类型上。

代码如下(示例):

List<Integer> numbers = Arrays.asList(0, 9 ,6 , 5, 6, 3, 2, 1, 12, 5, 8, 3, 9, 3);
//通过mapToInt转化
IntSummaryStatistics intStatus = numbers.stream().mapToInt((i) -> i).summaryStatistics();
System.out.println("列表中元素数量:" + intStatus.getCount());
System.out.println("列表中最大数 : " + intStatus.getMax());
System.out.println("列表中最小数 : " + intStatus.getMin());
System.out.println("所有数之和 : " + intStatus.getSum());
System.out.println("平均值 : " + intStatus.getAverage());

//输出:
列表中元素数量:14
列表中最大数 : 12
列表中最小数 : 0
所有数之和 : 72
平均值 : 5.142857142857143
流的终止操作
方法名称描述
count返回流中元素的总个数
max返回流中元素最大值
min返回流中元素最小值
findFirst返回流中第一个元素
findAny返回流中第一个元素(随机)
allMatch接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

代码如下(示例):

System.out.println("列表中的元素的总个数:" + intList.stream().count());
System.out.println("列表中的元素最大值:" + intList.stream().max(Integer::compareTo).get());
System.out.println("列表中的元素最小值:" + intList.stream().min(Integer::compareTo).get());
System.out.println("列表中的第一个元素:" + intList.stream().findFirst().get());
System.out.println("列表中的第一个元素(随机):" + intList.stream().findAny().get());
System.out.println("列表中的元素是否都>=1:" + intList.stream().allMatch(i -> i >= 1));
System.out.println("列表中的元素是否都不>1:" + intList.stream().noneMatch(i -> i > 1));
System.out.println("列表中的元素是否有一个>5:" + intList.stream().anyMatch(i -> i > 5));

//输出:
列表中的元素的总个数:8
列表中的元素最大值:10
列表中的元素最小值:1
列表中的第一个元素:1
列表中的第一个元素(随机):1
列表中的元素是否都>=1true
列表中的元素是否都不>1false
列表中的元素是否有一个>5true
创建流的两种方式
  • stream:为集合创建串行流
  • parallelStream:为集合创建并行流

代码如下(示例):

List<Integer> intList1 = Arrays.asList(1, 2, 3, 4, 5, 6);
System.out.print("stream串行:");
intList1.stream().forEach(System.out::print);
System.out.print("\nparallelStream并行:");
intList.parallelStream().forEach(System.out::print);
System.out.println();

//输出:
stream串行:123456
parallelStream并行:582961031
System.out.println("parallelStream并行forEach输出:");
intList1.parallelStream().forEach(i -> System.out.println("线程 = " + Thread.currentThread().getName() +" : 输出 = " + i));
System.out.println("parallelStream并行使用forEachOrdered顺序输出:");
intList1.parallelStream().forEachOrdered(i ->   System.out.println("线程 = " + Thread.currentThread().getName() +" : 输出 = " + i));

//输出:
parallelStream并行forEach输出:
线程 = main : 输出 = 4
线程 = ForkJoinPool.commonPool-worker-4 : 输出 = 1
线程 = ForkJoinPool.commonPool-worker-13 : 输出 = 6
线程 = ForkJoinPool.commonPool-worker-9 : 输出 = 3
线程 = ForkJoinPool.commonPool-worker-2 : 输出 = 5
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 2
parallelStream并行使用forEachOrdered顺序输出:
线程 = ForkJoinPool.commonPool-worker-2 : 输出 = 1
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 2
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 3
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 4
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 5
线程 = ForkJoinPool.commonPool-worker-6 : 输出 = 6

小结:

  • parallelStream 是利用多线程并行执行的,通过 parallelStream 可以很大程度简化我们使用并发操作。
  • 使用 parallelStream 是平行处理的,所以顺序每次都不一定一致,如果想要顺序是按照原来Stream的数据一样顺序输出,可以通过 forEachOrdered 方法实现。
  • 所以调用 forEachOrdered 方法顺序执行的话,就不是多线程并行处理了,是一个线程进行处理。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值