前言
Stream是Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)
Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。
特点
- 代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
- 多核友好:Java
函数式编程
使得编写并行程序如此简单,就是调用一下方法
流的操作过程为
流创建 => 中间操作 => 终端操作
流创建
集合数据创建为流中间操作
对数据进行处理终端操作
处理后的数据重新转换为集合对象
常见示例
List=>Array
方式1
1 | List<Expression> groupExps = new ArrayList<>(); |
方式2(不推荐)
1 | List<Expression> groupExps = new ArrayList<>(); |
流创建
Stream创建
1 | Stream<Integer> stream1 = Stream.of(1,2,3,4,5); |
Collection集合创建(应用中最常用的一种)
1 | List<Integer> integerList = new ArrayList<>(); |
Array数组创建
1 | int[] intArr = {1, 2, 3, 4, 5}; |
通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream
注:
使用数值流可以避免计算过程中拆箱装箱,提高性能。
Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流
文件创建
1 | try { |
通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行
函数创建
iterator
1 | Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(5); |
iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
generator
1 | Stream<Double> generateStream = Stream.generate(Math::random).limit(5); |
generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
中间操作
filter
用于通过设置的条件过滤出元素
1 | List strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
map
接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是”创建一个新版本”而不是去”修改”)
1 | List strings = Arrays.asList("abc", "abc", "bc", "efg", "abcd","jkl", "jkl"); |
distinct
返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流
1 | List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println); |
sorted
返回排序后的流
1 | List strings1 = Arrays.asList("abc", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
limit
会返回一个不超过给定长度的流
1 | List strings = Arrays.asList("abc", "abc", "bc", "efg", "abcd","jkl", "jkl"); |
skip
返回一个扔掉了前n个元素的流
1 | List strings = Arrays.asList("abc", "abc", "bc", "efg", "abcd","jkl", "jkl"); |
flatMap
使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流
1 | List strings = Arrays.asList("abc", "abc", "bc", "efg", "abcd","jkl", "jkl"); |
peek
对元素进行遍历处理
1 | public static void main(String[] args) { |
结果
[abc-110, ddd-110, kkk-110]
[abc, ddd, kkk]
以下两者是等效的
1 | package com.xhkjedu.test; |
也就是说
map可以操作原对象也可以生成新的对象。
peek只能操作原对象。
终端操作
Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。
终端操作的执行,才会真正开始流的遍历。如 count、collect 等
collect
收集器,将流转换为其他形式
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
Collector:结果收集策略的核心接口,具备将指定元素累加存放到结果容器中的能力;并在Collectors工具中提供了Collector接口的实现类
toList
将用户ID存放到List集合中
1 | List<Integer> idList = userList.stream().map(User::getId).collect(Collectors.toList()); |
toMap
将用户ID和Name以Key-Value形式存放到Map集合中
1 | Map<Integer,String> userMap = userList.stream().collect(Collectors.toMap(User::getId,User::getName)); |
toSet
将用户所在城市存放到Set集合中
1 | Set<String> citySet = userList.stream().map(User::getCity).collect(Collectors.toSet()); |
counting
符合条件的用户总数
1 | long count = userList.stream().filter(user -> user.getId()>1).collect(Collectors.counting()); |
summingInt
对结果元素即用户ID求和
1 | Integer sumInt = userList.stream().filter(user -> user.getId()>2).collect(Collectors.summingInt(User::getId)); |
minBy
筛选元素中ID最小的用户
1 | User maxId = userList.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getId))).get(); |
joining
将用户所在城市,以指定分隔符链接成字符串;
1 | String joinCity = userList.stream().map(User::getCity).collect(Collectors.joining("||")); |
groupingBy
按条件分组,以城市对用户进行分组;
1 | Map<String,List<User>> groupCity = userList.stream().collect(Collectors.groupingBy(User::getCity)); |
forEach
遍历流
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
findFirst
返回第一个元素
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
findAny
将返回当前流中的任意元素
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
count
返回流中元素总数
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
sum
求和
1 | int sum = userList.stream().mapToInt(User::getId).sum(); |
max
最大值
1 | int max = userList.stream().max(Comparator.comparingInt(User::getId)).get().getId(); |
min
最小值
1 | int min = userList.stream().min(Comparator.comparingInt(User::getId)).get().getId(); |
anyMatch
检查是否至少匹配一个元素,返回boolean
1 | List strings = Arrays.asList("abc", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
allMatch
检查是否匹配所有元素,返回boolean
1 | List strings = Arrays.asList("abc", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
noneMatch
检查是否没有匹配所有元素,返回boolean
1 | List strings = Arrays.asList("abc", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
reduce
可以将流中元素反复结合起来,得到一个值
1 | List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl"); |
orElse/orElseGet
相同点:
orElse(null)/orElseGet(null)
表示如果找不到值则返回null。orElse(value)/orElseGet(value)
可以设置默认值。如果找不到就会返回中设置的默认值。
不同点:
在使用方法时,
orElse
无论是否有值都会执行。orElseGet
如果有值,则不也会执行。
示例:
1 | package com.xhkjedu.test; |
结果
Optional[User{userName=’小明’, age=16, sex=’男’}]
无值-设置方法:
a:执行了方法
a:null
b:执行了方法
b:null
有值-设置方法:
c:执行了方法
c:User{userName=’小明’, age=16, sex=’男’}
d:User{userName=’小明’, age=16, sex=’男’}
设置默认值对比:
a1:User{userName=’神秘人’, age=0, sex=’未知’}
c1:User{userName=’小明’, age=16, sex=’男’}