使用java8也有一段时间了,但是一直没有整理过具体的笔记,现补充一下。
java8流处理让集合操作变得简便了很多,通过我们在jdk7的时候,要进行很多行代码的编写才能完成的操作,使用流以后,可以在一行代码之间实现。
我们先来准备一些基础数据,便于后面的操作。
/**
实体类
**/
package java8;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class People {
private int id;
private Stringname;
private int age;
private int score;
}
//main方法之前,先加载一些数据
static ListpeopleList =new ArrayList<>();
static ListsortList = Arrays.asList(1,3,8,6,7,9,10,5);
static {
People one =new People(1, "张三", 15, 88);
People two =new People(2, "李四", 14, 99);
People three =new People(3, "王五", 13, 100);
People four =new People(4, "赵六", 12, 85);
People five =new People(5, "王大麻子", 11, 55);
People six =new People(6, "田七", 10, 53);
People seven =new People(7, "小明", 14, 100);
People eight =new People(8, "小红", 15, 75);
People nine =new People(9, "小蓝", 17, 65);
People ten =new People(10, "小绿", 13, 66);
peopleList.add(one);
peopleList.add(two);
peopleList.add(three);
peopleList.add(four);
peopleList.add(five);
peopleList.add(six);
peopleList.add(seven);
peopleList.add(eight);
peopleList.add(nine);
peopleList.add(ten);
}
好了,数据我们已经准备好了,那么就开始下一步吧。先从简单的开始说起。
- forEach
//最简单的遍历,就是把当前peopleList循环出来,并打印。之前我们最常用的就是for循环,现在一行就可以搞定了。
peopleList.stream().forEach(people -> System.out.println(people));
2.findAny, findFirst,从语意上可以看出来,两个方法都是查找。findAny就是查找符合条件的任意一条;findFirst查找符合条件的第一条。l因findFirst和findAny的使用方式,完全一致,就不再重复了。
Optional<People> any = peopleList.stream().findAny();
if(any.isPresent()){
System.out.println(any.get());
}
//orElse和orElseGet这两个方法看起来差不多.区别在于:不论findAny返回的结果是不是空,orElse都会执行,而orElseGet则当findAny为空时才会执行。
//只有当默认值已经事先定义的情况下,才使用orElse(),否则使用orElseGet()更好。
People people = peopleList.stream().findAny().orElse(new People(12, "ls", 13, 66));
System.out.println(people);
People people1 = peopleList.stream().findAny().orElseGet(()->new People(11, "zs", 12, 22));
System.out.println(people1);
//获取不到则抛出异常,这个异常信息,根据业务需要来就可以了。
People people2 = peopleList.stream().findAny().orElseThrow(RuntimeException::new);
System.out.println(people2.toString());
- filter过滤,根据条件进行筛选数据。
//filter 根据自己的需求过滤出自己所需要的内容,比如过滤出年龄大于15岁的people
List<People> filterPeople = peopleList.stream().filter(people -> people.getAge() > 15).collect(Collectors.toList());
filterPeople.stream().forEach(System.out::println);
4.map和flatMap,map是一维映射,也就是说,只有一层的List,使用其中的对象的某个属性是可以的。但是如果List嵌套,则只有第一层才能被映射。如果你想把两层List都映射出来。那么就请使用flatMap.
//map 抽取对象的某一个属性变成新的List,用名字形成一个新的List(一维,也就是说,只能使用一层List,如果List里面放一个List,此时map就只能把第一层List映射出来)
List<String> peopleNamesList = peopleList.stream().map(People::getName).collect(Collectors.toList());
peopleNamesList.stream().forEach(System.out::println);
//flatMap 跟map的区别就在于,flatMap可以把多层次的对象给映射出来
List<List<Integer>> outer = new ArrayList<>();
List<Integer> inner1 = new ArrayList<>();
inner1.add(1);
List<Integer> inner2 = new ArrayList<>();
inner2.add(2);
List<Integer> inner3 = new ArrayList<>();
inner3.add(3);
List<Integer> inner4 = new ArrayList<>();
inner4.add(4);
List<Integer> inner5 = new ArrayList<>();
inner5.add(5);
outer.add(inner1);
outer.add(inner2);
outer.add(inner3);
outer.add(inner4);
outer.add(inner5);
List<Integer> result = outer.stream().flatMap(List::stream).collect(toList());
System.out.println(result);
5.sorted 排序,分别使用了对象的属性排序,和Integer的排序。
//sorted 排序,默认是正序,可以指定对象的任意属性来进行排序。.reversed是反转的意思,正序的反转就相当于倒序排序。如果是基础数据类型,则可以直接使用sorted来排序,没有任何参数的时候,相当于传入Comparator.naturalOrder(),即正序排序。Comparator.reverseOrder(), 则是倒序排序。
peopleList.stream().sorted(Comparator.comparing(People::getId).reversed()).forEach(System.out::println);
sortList.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
//写到这里,不得不提一下我之前犯下的一个错误。集合的sort()是非线程安全的。
for (int i = 0; i < 10; i++) {
new Thread(() -> {
peopleList.sort((o1,o2) -> o1.getScore()-o2.getScore());
}).start();
}
error.png
//而 stream 的 sorted是线程安全的。
for (int i = 0; i < 10; i++) {
new Thread(() -> {
peopleList.stream().sorted((o1,o2) -> o1.getScore()-o2.getScore()).forEach(System.out::println);
}).start();
}
下面这种写法就可以正常返回结果。
- collect 一般是一个stream操作的最后一步,把最终结果转成map或者list或者set。
//collect 一般是一个stream操作的最后一步,把最终结果转成map或者list或者set。
//其中转成map的时候,有groupingBy和groupingByConcurrent(可以利用到多核架构的能力。groupingByConcurrent也有3个重载的方法,与groupingBy类似。但返回值必须是ConcurrentHashMap或其子类.)
//转成list
peopleList.stream().collect(Collectors.toList());
//转成Map
Map<Integer, List<People>> collect = peopleList.stream().collect(Collectors.groupingBy(people -> people.getAge()));
collect.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue().size());
});
//按照多个字段进行分组
Map<Integer, Map<Integer, List<People>>> mutGroupMap = peopleList.stream().collect(Collectors.groupingBy(People::getAge, Collectors.groupingBy(People::getScore)));
mutGroupMap.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue().size());
});
//注意,当我们的集合当中有重复数据的时候,groupingby会把结果改为数组:
peopleList.add(new People(10,"小绿", 13,66));
Map<String, Map<Integer, List<People>>> flatMap = peopleList.stream().collect(Collectors.groupingBy(People::getName, Collectors.groupingBy(People::getScore)));
flatMap.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});
1.png
groupingby 优点:非常适合分组归类使用,方便快捷。
缺点也很明显,当分组的条件多的时候,这个Map就难以阅读,维护了。
groupingby不支持null键
//toMap 速度快,支持一个null键
//添加一个重复数据
peopleList.add(new People(10,"小绿", 13,66));
//报异常
Map<String, Integer> toMap2 = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge));
toMap2.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + "--" + entry.getValue());
});
//Exception in thread "main" java.lang.IllegalStateException: Duplicate key 13
//at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
//at java.util.HashMap.merge(HashMap.java:1254)
//at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
//at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
//at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
//at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
//at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
//at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
//at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
//at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
//at com.hexy.java8.GroupingByAndToMap.main(GroupingByAndToMap.java:43)
Map<String, Integer> toMap = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge ,(oldValue, newValue) -> oldValue , HashMap::new));
toMap.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + "--" + entry.getValue());
});
//正常打印,我们来看一下,如果我们不写第三个参数,那jdk默认帮我们做了什么吧?
1.png
2.png
7.max min 根据对象的某个属性取最大值或者最小值
//max min 根据对象的某个属性取最大值或者最小值,比如取分数最高的,和分数最低的 具体的对比规则,可以直接使用Comparator传入
Optional<People> maxScore = peopleList.stream().max(Comparator.comparing(People::getScore));
Optional<People> minScore = peopleList.stream().min(Comparator.comparing(People::getScore));
System.out.println(maxScore.get());
System.out.println(minScore.get());
- limit 这个跟sql的limit很类似。只取前N个。
//limit 限制取 limit个 成绩大于90的前两个(和一个例子,只是找出来了成绩大于90分的人,但是并没有数量上的限制,所以找出来3人,但是第二个,由于写了limit(2),则就是限制,我只取两条)
List<People> greatThanNinetyList = peopleList.stream().filter(people -> people.getScore() > 90).collect(Collectors.toList());
System.out.println(greatThanNinetyList.size());
List<People> greatThanNinetyLimitTwoList = peopleList.stream().filter(people -> people.getScore() > 90).limit(2).collect(Collectors.toList());
System.out.println(greatThanNinetyLimitTwoList.size());
9.count 集合的数量
System.out.println(peopleList.stream().count());
10.distinct 这个跟sql的distinct也很类似
//去重, 我们再加入一个小绿 10, "小绿", 13, 66
peopleList.add(new People(10, "小绿", 13, 66));
//应该有11个
System.out.println("distinct前的数量:" + peopleList.size());
//有10个
System.out.println("distinct后的数量:" + peopleList.stream().distinct().collect(Collectors.toList()).size());
11.allMatch(全部符合设置的条件) anyMatch(任意一个符合设置的条件) noneMatch(没有一个符合设置的条件)返回值为boolean
// allMatch(全部符合设置的条件) anyMatch(任意一个符合设置的条件) noneMatch(没有一个符合设置的条件)
boolean allMatch = peopleList.stream().allMatch(people -> people.getScore()>99);
System.out.println(allMatch);
boolean anyMatch = peopleList.stream().anyMatch(people -> people.getScore()>99);
System.out.println(anyMatch);
boolean noneMatch = peopleList.stream().anyMatch(people -> people.getScore()>100);
System.out.println(noneMatch);
12.skip 跳过指定个数的元素
//只打印出peopleList的后7个元素
peopleList.stream().skip(3).forEach(System.out::println);
13.peek 是一个中间操作,它一般用于调试,或者你需要修改stream的元素的时候,使用它比较合适。因为它改变了数据之后,并不会消耗掉stream,你可以继续的执行后面的操作。
peopleList.stream().peek(people -> {
if(people.getScore() == 100){
people.setScore(1000);
}
}).forEach(System.out::println);
13.foreachOrdered和unordered 在并发情况下,forechOrdered是必须按照集合的顺序来循环。而unordered则是在并发条件下,取消必须按集合顺序循环。
//foreachOrdered 严格按照list的顺序输出(一般只有并行的时候用)
peopleList.stream().parallel().forEach(System.out::println);
System.out.println("----------------------------");
// 取消严格按照顺序循环,有利于执行效率的提高
peopleList.stream().unordered().parallel().forEach(System.out::println);
- reduce
//reduce 1.第一个重载函数的入参是一个BinaryOperator的函数式接口(该函数式接口接收两个T类型的参数并返回T类型)。该方法返回的是一个Optional类型的值。
sortList.stream().reduce((item, next)-> item + next).ifPresent(System.out::println);
//reduce 2.
Integer reduce = sortList.stream().reduce(0, (item, next) -> item + next);
System.out.println(reduce);
//reduce 3.
Integer reduce1 = sortList.stream().reduce(0, (item, next) -> item + next, (item, next) -> item);
System.out.println(reduce1);
15.parallel 开启并行操作
//parallel 把当前流设置为并行,isParallel是检测当前Stream是否是并行。
//false 因为没有开启并行
System.out.println(peopleList.stream().isParallel());
//true 因为手动开启了并行
System.out.println(peopleList.stream().parallel().isParallel());
16.sequential 返回串行的流,也就是取消并行
peopleList.stream().sequential();
17.Spliterator是一个可分割迭代器(splitable iterator),对于并行处理的能力大大增强,Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator。
Spliterator<People> mySpliterator = peopleList.stream().spliterator();
AtomicInteger count = new AtomicInteger(0);
for(int i=0;i<4;i++){
new Thread(()->{
String threadName = Thread.currentThread().getName();
System.out.println("线程"+threadName+"开始运行-----");
mySpliterator.trySplit().forEachRemaining(people -> {
if(people.getScore()>80){
int num = people.getScore();
count.addAndGet(num);
System.out.println("数值:"+num+"------"+threadName);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("线程"+threadName+"运行结束-----");
}).start();
}
//得到的结果
线程Thread-2开始运行-----
线程Thread-0开始运行-----
数值:100------Thread-0
数值:88------Thread-2
线程Thread-3开始运行-----
线程Thread-1开始运行-----
线程Thread-1运行结束-----
线程Thread-3运行结束-----
线程Thread-0运行结束-----
数值:99------Thread-2
数值:100------Thread-2
数值:85------Thread-2
线程Thread-2运行结束-----
网友评论