Stream 流 背景介绍 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Data @AllArgsConstructor @NoArgsConstructor public class Student implements Serializable { @ApiModelProperty ("学生id" ) public Integer id; @ApiModelProperty ("学生姓名" ) public String name; @ApiModelProperty ("学生年龄" ) public Integer age; @ApiModelProperty ("学生生日" ) public Date birthday; private static final long serialVersionUID = 1L ; }
添加数据
1 2 3 4 5 6 7 8 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd" ); ArrayList<Student> students = new ArrayList<>(); students.add(new Student(1 , "张三" , 20 , dateFormat.parse("2020-10-01" ))); students.add(new Student(2 , "萧炎" , 22 , dateFormat.parse("2020-10-02" ))); students.add(new Student(3 , "唐三" , 18 , dateFormat.parse("2020-09-10" ))); students.add(new Student(4 , "牧尘" , 20 , dateFormat.parse("2020-05-20" ))); students.add(new Student(5 , "林动" , 16 , dateFormat.parse("2020-08-09" )));
Stream对象的创建 Stream对象分为两种,一种串行的流对象,一种并行的流对象。
1 2 3 4 5 Stream<Student> stream = students.stream(); Stream<Student> parallelStream = students.parallelStream();
forEach Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:
1 2 Random random = new Random(); random.ints().limit(10 ).forEach(System.out::println);
filter 对Stream中的元素进行过滤操作,当设置条件返回true时返回相应元素。
1 2 3 4 List<UmsPermission> dirList = permissionList.stream() .filter(permission -> permission.getType() == 0 ) .collect(Collectors.toList());Copy to clipboardErrorCopied
map 对Stream中的元素进行转换处理后获取。比如可以将UmsPermission对象转换成Long对象。 我们经常会有这样的需求:需要把某些对象的id提取出来,然后根据这些id去查询其他对象,这时可以使用此方法。
1 2 3 4 5 6 7 List<Long> idList = permissionList.stream() .map(permission -> permission.getId()) .collect(Collectors.toList());Copy to clipboardErrorCopied List<String> collect = joinCircles.stream().filter(joinCircle -> joinCircle.getUserSignin() < 10 ).distinct() .map(JoinCircle::getUserId).collect(Collectors.toList());
limit 从Stream中获取指定数量的元素。
1 2 3 4 List<UmsPermission> firstFiveList = permissionList.stream() .limit(5 ) .collect(Collectors.toList());Copy to clipboardErrorCopied
count 仅获取Stream中元素的个数。
1 2 3 4 long dirPermissionCount = permissionList.stream() .filter(permission -> permission.getType() == 0 ) .count();Copy to clipboardErrorCopied
sorted 对Stream中元素按指定规则进行排序。
对单个属性排序:
根据年龄升序排序
1 2 3 students.stream() .sorted(Comparator.comparing(Student::getAge)) .collect(Collectors.toList());
根据年龄降序排序(先升序,后逆序)
1 2 3 students.stream() .sorted(Comparator.comparing(Student::getAge).reversed()) .collect(Collectors.toList());
这个是先根据年龄升序排序,然后利用reversed()逆序;
根据年龄降序排序(直接逆序)
1 2 3 students.stream() .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder())) .collect(Collectors.toList());
利用Comparator.reverseOrder()直接就是降序排序
对多个属性排序
根据年龄降序 ,生日升序
1 2 3 4 students.stream() .sorted(Comparator.comparing(Student::getAge).reversed() .thenComparing(Student::getBirthday)) .collect(Collectors.toList());
1 2 3 4 students.stream() .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder()) .thenComparing(Student::getBirthday)) .collect(Collectors.toList());
第一种是先按照年龄升序,然后逆序,第二种则是直接按照年龄降序
根据年龄降序 ,生日降序
1 2 3 4 students.stream() .sorted(Comparator.comparing(Student::getAge) .thenComparing(Student::getBirthday).reversed()) .collect(Collectors.toList());
这里要明白为什么只是用了一次reversed()年龄为什么也逆序了,reversed()的作用域是reversed()前面的所有的排序,也就是作用域为年龄和生日,如果想按照年龄升序,生日降序: 则在年龄后面在加上一个reversed()逆序两次也就是升序了
1 2 3 4 students.stream() .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder()) .thenComparing(Student::getBirthday,Comparator.reverseOrder())) .collect(Collectors.toList());
Comparator自定义比较器:
int compare(Object o1, Object o2);
1、比较者大于被比较者,那么返回正整数 2、比较者等于被比较者,那么返回0 3、比较者小于被比较者,那么返回负整数
定义一个类实现Comparator接口
1 2 3 4 5 6 7 public class StudentComparator implements Comparator <Student > { @Override public int compare (Student o1, Student o2) { return o1.getAge() - o2.getAge(); } }
使用该排序器
1 students.stream().sorted(new StudentComparator()).collect(Collectors.toList());
自定义比较复杂的排序算法,利用stream流排序(可以去详细了解stream流)分页获取数据例如:
1 students.stream().sorted(newStudentComparator()).skip(5 ).limit(10 ).collect(Collectors.toList());
小结 逆序存在两种写法:
Comparator.comparing(Student::getAge).reversed()
Comparator.comparing(Student::getAge,Comparator.reverseOrder())
第一种写法会逆序之前的全部排序规则,如果思路不清晰容易出错,如果要排序的全部字段都按照降序,推荐使用这个,直接在最后添加reversed()就好。
但是如果比较比较复杂,使用Comparator.reverseOrder()比较稳妥一些。
skip 跳过指定个数的Stream中元素,获取后面的元素。
1 2 3 4 List<UmsPermission> skipList = permissionList.stream() .skip(5 ) .collect(Collectors.toList());Copy to clipboardErrorCopied
distinct 剔除重复的元素
Collectors Collectors.toList() 1 2 List<Student> studentList = students.stream().filter(student -> student.getAge() > 20 ).collect(Collectors.toList());
[Student(id=2, name=萧炎, age=22, birthday=Fri Oct 02 00:00:00 CST 2020)]
Collectors.joining()
Collectors.joining()
Collectors.joining(delimiter)
Collectors.joining(delimiter,prefix,suffix)
1 2 3 String studentStr1 = students.stream().map(Student::getName).collect(Collectors.joining()); String studentStr2 = students.stream().map(Student::getName).collect(Collectors.joining("," )); String studentStr3 = students.stream().map(Student::getName).collect(Collectors.joining("," , "武动乾坤->" , "<-斗破苍穹" ));
张三萧炎唐三牧尘林动 张三,萧炎,唐三,牧尘,林动 武动乾坤->张三,萧炎,唐三,牧尘,林动<-斗破苍穹
Collectors.toSet() 1 Set<Integer> ageSet = students.stream().map(Student::getAge).collect(Collectors.toSet());
[16, 18, 20, 22]
Collectors.toMap()/Collectors.toConcurrentMap()
Collectors.toMap(p1,p2);
Collectors.toMap(p1,p2,p3);
Collectors.toMap(p1,p2,p3,p4);
p1: 要转换为的map的键
p2:要转换为的map的值,如果要转换为本对象则可设置为Function.identity()
p3:用于解决键的冲突,(o1,o2)-> o1 如果冲突选择前面的那个值,如果不设置冲突会造成异常
p4:设置要转换为的Map类型,如果不设置就为Map/ConcurrentMap
1 Map<Integer, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity()));
1 Map<Integer, Student> outStudentMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (o1, o2) -> o2));
1 ConcurrentHashMap<Integer, Student> studentConcurrentHashMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (o1, o2) -> o2, ConcurrentHashMap::new ));
略,可以尝试键相同的情况下,不设置param3所产生异常!
Collectors.toConcurrentMap()的所有都和Collectors.toMap()相同!!除了返回的Map类型
Collectors.groupingBy()/Collectors.groupingByConcurrent()
Collectors.groupingBy(p1)
Collectors.groupingBy(p1,p2)
Collectors.groupingBy(p1,p2,p3)
p1:按照什么来进行分组
p2:分组完成后用什么容器装载数据 默认Map
p3:分类后,对应的分类结果用什么容器装载 默认List
1 2 3 ConcurrentHashMap<String, List<Student>> groupStudentByGrade = students.stream().collect(Collectors.groupingBy(Student::getGrade,ConcurrentHashMap::new , Collectors.toList())); Map<String, Long> groupCountingByGrade = students.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.counting()));
1 Map<String, List<Student>> groupStudentByGradeAndAge = students.stream().collect(Collectors.groupingBy(student -> student.getGrade() + "," + student.getAge()));
{高三=[Student(id=5, name=林动, age=16, birthday=Sun Aug 09 00:00:00 CST 2020, grade=高三)],
高二=[Student(id=2, name=萧炎, age=22, birthday=Fri Oct 02 00:00:00 CST 2020, grade=高二), Student(id=4, name=牧尘, age=20, birthday=Wed May 20 00:00:00 CST 2020, grade=高二)],
高一=[Student(id=1, name=张三, age=20, birthday=Thu Oct 01 00:00:00 CST 2020, grade=高一), Student(id=3, name=唐三, age=18, birthday=Thu Sep 10 00:00:00 CST 2020, grade=高一)]}
{高三=1, 高二=2, 高一=2}
结果三:略 根据年级和年龄分类无意义,只是为了展示多个条件分组
这个分组远远不止这点东西,其他的可以自行了解这个
Collectors.partitioningBy 分割列表 一个为false 一个为true
1 Map<Boolean, List<Student>> partitioningStudent = students.stream().collect(Collectors.partitioningBy(student -> student.getAge() > 20 ));
{false=[Student(id=1, name=张三, age=20, birthday=Thu Oct 01 00:00:00 CST 2020, grade=高一), Student(id=3, name=唐三, age=18, birthday=Thu Sep 10 00:00:00 CST 2020, grade=高一), Student(id=4, name=牧尘, age=20, birthday=Wed May 20 00:00:00 CST 2020, grade=高二), Student(id=5, name=林动, age=16, birthday=Sun Aug 09 00:00:00 CST 2020, grade=高三)],
true=[Student(id=2, name=萧炎, age=22, birthday=Fri Oct 02 00:00:00 CST 2020, grade=高二)]}
统计 另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。
1 2 3 4 5 6 7 8 List<Integer> agesList = students.stream().map(Student::getAge).collect(Collectors.toList()); IntSummaryStatistics intSummaryStatistics = agesList.stream().mapToInt(x -> x).summaryStatistics(); System.out.println("获取最大的年龄:" + intSummaryStatistics.getMax()); System.out.println("获取最小的年龄:" + intSummaryStatistics.getMin()); System.out.println("所有年龄之和:" + intSummaryStatistics.getSum()); System.out.println("获取年龄的平均是:" + intSummaryStatistics.getAverage()); System.out.println("获取年龄个数:" + intSummaryStatistics.getCount());
stream 不仅仅是这些,这只是基本的使用
全部代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd" ); ArrayList<Student> students = new ArrayList<>(); students.add(new Student(1 , "张三" , 20 , dateFormat.parse("2020-10-01" ), "高一" )); students.add(new Student(2 , "萧炎" , 22 , dateFormat.parse("2020-10-02" ), "高二" )); students.add(new Student(3 , "唐三" , 18 , dateFormat.parse("2020-09-10" ), "高一" )); students.add(new Student(4 , "牧尘" , 20 , dateFormat.parse("2020-05-20" ), "高二" )); students.add(new Student(5 , "林动" , 16 , dateFormat.parse("2020-08-09" ), "高三" )); List<Student> studentList = students.stream().filter(student -> student.getAge() > 20 ).collect(Collectors.toList()); String studentStr1 = students.stream().map(Student::getName).collect(Collectors.joining()); String studentStr2 = students.stream().map(Student::getName).collect(Collectors.joining("," )); String studentStr3 = students.stream().map(Student::getName).collect(Collectors.joining("," , "武动乾坤->" , "<-斗破苍穹" )); Set<Integer> ageSet = students.stream().map(Student::getAge).collect(Collectors.toSet()); Map<Integer, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity())); Map<Integer, Student> outStudentMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (o1, o2) -> o2)); ConcurrentHashMap<Integer, Student> studentConcurrentHashMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (o1, o2) -> o2, ConcurrentHashMap::new )); ConcurrentHashMap<String, List<Student>> groupStudentByGrade = students.stream().collect(Collectors.groupingBy(Student::getGrade, ConcurrentHashMap::new , Collectors.toList())); Map<String, List<Student>> groupStudentByGradeAndAge = students.stream().collect(Collectors.groupingBy(student -> student.getGrade() + "," + student.getAge())); Map<String, Long> groupCountingByGrade = students.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.counting())); Map<Boolean, List<Student>> partitioningStudent = students.stream().collect(Collectors.partitioningBy(student -> student.getAge() > 20 )); System.out.println("Collectors.toList()-->" + studentList); System.out.println("Collectors.joining()-->" + studentStr1); System.out.println("Collectors.joining(delimiter)-->" + studentStr2); System.out.println("Collectors.joining(delimiter,prefix,suffix)-->" + studentStr3); System.out.println("Collectors.toSet()-->" + ageSet.toString()); System.out.println("Collectors.toMap(p1, p2)-->" + studentMap.toString()); System.out.println("Collectors.toMap(p1, p2, p3)-->" + outStudentMap.toString()); System.out.println("Collectors.toMap(p1, p2, p3, p4)" + studentConcurrentHashMap.toString()); System.out.println("Collectors.groupingBy()单条件分组-->" + groupStudentByGrade); System.out.println("Collectors.groupingBy()多条件分组-->" + groupStudentByGradeAndAge); System.out.println("Collectors.groupingBy()计数-->" + groupCountingByGrade); System.out.println("Collectors.partitioningBy()" + partitioningStudent);