本文是我在学习Java8的时候参考下面大佬的文章拷贝的,仅用于个人整理和学习用途
原文地址://www.greatytc.com/p/0bb4daf6c800
一. 什么是 Stream
Stream 中文称为“流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。
换句话说,你只需要告诉流你的要求,流便会在背后自行根据要求对元素进行处理,而你只需要 “坐享其成”。
二. 流操作
整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理。
其中数据源便是原始集合,然后将如 List<T> 的集合转换为 Stream<T> 类型的流,并对流进行一系列的中间操作,比如过滤保留部分元素、对元素进行排序、类型转换等;最后再进行一个终端操作,可以把 Stream 转换回集合类型,也可以直接对其中的各个元素进行处理,比如打印、比如计算总数、计算最大值等等
很重要的一点是,很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,我们来看看一条 Stream 操作的代码:
如果是以前,进行这么一系列操作,你需要做个迭代器或者 foreach 循环,然后遍历,一步步地亲力亲为地去完成这些操作;但是如果使用流,你便可以直接声明式地下指令,流会帮你完成这些操作。
有没有想到什么类似的?是的,就像 SQL 语句一样,select username from user where id = 1,你只要说明:“我需要 id 是 1 (id = 1)的用户(user)的用户名(username)”,那么就可以得到自己想要的数据,而不需要自己亲自去数据库里面循环遍历查找。
三.方法介绍与实践:
1.获取流:stream() / parallelStream()
List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
Stream<Integer> stream = asList.stream();
System.out.println(stream);
输出结果:XX.stream() 方法获取的是流,然而我们在实际开发中一般需要获取的是集合
2.流转换为集合:collect(Collectors.toList())
List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
List<Integer> collect1 = asList.stream().collect(Collectors.toList());
List<Integer> collect2 = asList.stream().collect(toList());
System.out.println(collect1);
System.out.println(collect2);
输出结果: 这两种写法获取的结果都是一样的,第二种为简写
3.排序 sorted()
eg1:
List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
List<Integer> asc = asList.stream().sorted().collect(toList()); //正序
List<Integer> desc = asList.stream().sorted(Comparator.reverseOrder()).collect(toList());//倒序
System.out.println(asc);
System.out.println(desc);
输出结果:eg2:使用mybatis-plus 查询后对获得的数组中的某个字段进行排序(数据有点多截取了前5条数据进行排序)
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<>()); //查询表中所有数据
////按照ATag中SerialNumber字段排序
List<ATag> collect1 = aTags.stream().sorted(Comparator.comparing(ATag::getSerialNumber)).limit(5).collect(toList());
List<ATag> collect2 = aTags.stream().sorted(Comparator.comparing(ATag::getSerialNumber).reversed()).limit(5).collect(toList());
collect1.forEach(System.out::println);
System.out.println("----------分割线------------");
collect2.forEach(System.out::println);
输出结果:
4.去重 distinct()
eg1:
List<Integer> asList = Arrays.asList(10,10,20,20,30,40,50);
asList.stream().distinct().collect(toList()).forEach(System.out::println);
输出:
eg2:
如果想要对 List<Object> 中某个字段进行去重这里使用 distinct(类::字段) 这种方法是不行的(没有这种筛选条件)
因此,需要借助一下过滤器以及下面学到的filter()方法,实现对 List<Object> 中根据某个字段进行去重
参考文章://www.greatytc.com/p/77c3b503730c
//过滤器:
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>(16);
return t -> map.putIfAbsent(keyExtractor.apply(t),Boolean.TRUE) == null;
}
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));//查询表中serial_number=6的数据
aTags.forEach(System.out::println);
System.out.println("-------------------------分割线-------------------------");
aTags.stream().filter(distinctByKey(ATag::getNameZh)).collect(toList()).forEach(System.out::println);
输出 :5.过滤 filter()
保留 boolean 为 true 的元素
//查询表中serial_number=6的数据
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
aTags.stream().filter(x->x.getNameZh().equals("御坂美琴")).collect(toList()).forEach(System.out::println);
输出:6.截取(返回前 n 个元素) limit(long n)
//查询表中serial_number=6的数据
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
aTags.stream().limit(2).forEach(System.out::println);
输出结果:7.跳过 skip(long n)
List<Integer> asList = Arrays.asList(10,20,30,40,50);
System.out.println(asList.stream().skip(1).limit(2).collect(toList()));
这里使用了skip()和limit()
tips:
用在 limit(n) 前面时,先去除前 m 个元素再返回剩余元素的前 n 个元素
limit(n) 用在 skip(m) 前面时,先返回前 n 个元素再在剩余的 n 个元素中去除 m 个元素
8. map(T -> R)
将流中的每一个元素 T 映射为 R(类似类型转换)
eg1:
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
aTags.stream().map(ATag::getNameZh).collect(toList()).forEach(System.out::println);
这里相当于把List<ATag>转换为List<String>
List<Integer> asList = Arrays.asList(10,20,30,40,50);
List<Integer> collect = asList.stream().map(x -> x + x * 0.1).mapToInt(T -> T.intValue()).boxed().collect(toList());
输出:9. flatMap(T -> Stream<R>)
将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());
上面例子中,我们的目的是把 List 中每个字符串元素以" "分割开,变成一个新的 List<String>。
首先 map 方法分割每个字符串元素,但此时流的类型为 Stream<String[ ]>,因为 split 方法返回的是 String[ ] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream<String> 流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 Stream<String>
10.anyMatch(T -> boolean)
流中是否有一个元素匹配给定的 T -> boolean 条件
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
System.out.println(aTags.stream().anyMatch(x -> x.getNameEn().equals("Misaka Miko")));
输出:11. allMatch(T -> boolean)
流中是否所有元素都匹配给定的 T -> boolean 条件,当流中所有元素都符合条件的时候才返回true
List<Integer> asList = Arrays.asList(10,20,30,40,50);
System.out.println(asList.stream().allMatch(x -> x >= 10));
输出:12.noneMatch(T -> boolean)
流中是否没有元素匹配给定的 T -> boolean 条件(流中有元素匹配到条件返回true否则返回false)
List<Integer> asList = Arrays.asList(10,20,30,40,50);
System.out.println(asList.stream().noneMatch(x -> x >= 10));
输出:
13. findAny() 和 findFirst()
findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)
findFirst():找到第一个元素
List<Integer> asList = Arrays.asList(10,20,30,40,50);
System.out.println(asList.stream().findFirst());
System.out.println(asList.stream().findAny());
System.out.println(asList.parallelStream().findAny());
输出:
14.reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
用于组合流中的元素,如求和,求积,求最大值等
eg1:其中,reduce 第一个参数 0 代表起始值为 0,lambda (a, b) -> a + b 即将两值相加产生一个新值
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
System.out.println(aTags.stream().map(ATag::getParentId).reduce(0L, Long::sum));
输出:eg2:
如果没有初始值,需要考虑结果可能不存在的情况,因此返回的是 Optional 类型如果不需要这种返回类型设置初始值即可
List<Integer> asList = Arrays.asList(10,20,30,40,50);
Integer reduce1 = asList.stream().reduce(0, (x, y) -> x + y);
Integer reduce2 = asList.stream().mapToInt(T -> T.intValue()).boxed().reduce(0, (x, y) -> x + y);
Optional<Integer> reduce3 = asList.stream().reduce((x, y) -> x + y);
System.out.println("reduce1:"+reduce1 +"\n"+ "reduce2:"+reduce2+"\n"+"reduce3:"+reduce3);
15. count()
返回流中元素个数,结果为 long 类型
List<Integer> asList = Arrays.asList(10,20,30,40,50);
asList.stream().count();
16.遍历流中的每一个元素 forEach()
当然我们也可以通过forEach()遍历流中的每一个元素,然后注入Mapper对流中元素进行CRUD
List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
aTags.forEach(System.out::println);
System.out.println("-------------------分割线-------------------------");
aTags.stream().forEach(item->{
System.out.println(item.getNameZh());
});
输出:文章出处
作者:Howie_Y
链接://www.greatytc.com/p/0bb4daf6c800
来源:简书