java8 新特性 stream的使用

Stream 流

背景介绍

@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;
    @ApiModelProperty("学生年级")
    public String grade;
    private static final long serialVersionUID = 1L;
}

添加数据

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对象分为两种,一种串行的流对象,一种并行的流对象。

// permissionList指所有权限列表
// 为集合创建串行流对象
Stream<Student> stream = students.stream();
// 为集合创建并行流对象
Stream<Student> parallelStream = students.parallelStream();

forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random(); 
random.ints().limit(10).forEach(System.out::println);

filter

对Stream中的元素进行过滤操作,当设置条件返回true时返回相应元素。

// 获取权限类型为目录的权限
List<UmsPermission> dirList = permissionList.stream()
    .filter(permission -> permission.getType() == 0)
    .collect(Collectors.toList());Copy to clipboardErrorCopied

map

对Stream中的元素进行转换处理后获取。比如可以将UmsPermission对象转换成Long对象。 我们经常会有这样的需求:需要把某些对象的id提取出来,然后根据这些id去查询其他对象,这时可以使用此方法。

// 获取所有权限的id组成的集合
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中获取指定数量的元素。

// 获取前5个权限对象组成的集合
List<UmsPermission> firstFiveList = permissionList.stream()
    .limit(5)
    .collect(Collectors.toList());Copy to clipboardErrorCopied

count

仅获取Stream中元素的个数。

// count操作:获取所有目录权限的个数
long dirPermissionCount = permissionList.stream()
    .filter(permission -> permission.getType() == 0)
    .count();Copy to clipboardErrorCopied

sorted

对Stream中元素按指定规则进行排序。

对单个属性排序:

  1. 根据年龄升序排序
students.stream()
    .sorted(Comparator.comparing(Student::getAge))
    .collect(Collectors.toList());
  1. 根据年龄降序排序(先升序,后逆序)
students.stream()
  .sorted(Comparator.comparing(Student::getAge).reversed())
  .collect(Collectors.toList());

这个是先根据年龄升序排序,然后利用reversed()逆序;

  1. 根据年龄降序排序(直接逆序)
students.stream()
 .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder()))
 .collect(Collectors.toList());

利用Comparator.reverseOrder()直接就是降序排序


对多个属性排序
  1. 根据年龄降序,生日升序
students.stream()
 .sorted(Comparator.comparing(Student::getAge).reversed()
 .thenComparing(Student::getBirthday))
 .collect(Collectors.toList());
students.stream()
 .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder())
 .thenComparing(Student::getBirthday))
 .collect(Collectors.toList());

第一种是先按照年龄升序,然后逆序,第二种则是直接按照年龄降序

  1. 根据年龄降序,生日降序
students.stream()
 .sorted(Comparator.comparing(Student::getAge)
 .thenComparing(Student::getBirthday).reversed())
 .collect(Collectors.toList());

这里要明白为什么只是用了一次reversed()年龄为什么也逆序了,reversed()的作用域是reversed()前面的所有的排序,也就是作用域为年龄和生日,如果想按照**年龄升序,生日降序:**则在年龄后面在加上一个reversed()逆序两次也就是升序了

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接口

public class StudentComparator implements Comparator<Student> {   
    @Override
    public int compare(Student o1, Student o2) {
        // 这里可以自己定义复杂的排序算法
        return o1.getAge() - o2.getAge();
    }
}

使用该排序器

students.stream().sorted(new StudentComparator()).collect(Collectors.toList());

自定义比较复杂的排序算法,利用stream流排序(可以去详细了解stream流)分页获取数据例如:

students.stream().sorted(newStudentComparator()).skip(5).limit(10).collect(Collectors.toList());

综合日常使用:

List<Student> finStudent =
       students.stream()
               // 去重
               .distinct()
               .filter(student -> {
                   // 这里只是演示一下复杂的过滤怎么实现,这里可以直接一行表示,不需要大括号!
                   boolean filter = student.name.contains("三") && student.getAge() > 10;
                   return filter;
               })
               // 根据自定义的排序器(根据学生年龄进行升序),然后根据学生的生日进行降序 这里最好不要使用.reversed()若使用这个会导致年龄逆序的,除非前面也加上.reversed()
               .sorted(new StudentComparator().thenComparing(Student::getBirthday, Comparator.reverseOrder()))
               // skip跳过0个数据 limit获取10个数据 和数据库中分页 limit 0,10 一样
               .skip(0).limit(10).collect(Collectors.toList());
小结

逆序存在两种写法:

  • Comparator.comparing(Student::getAge).reversed()
  • Comparator.comparing(Student::getAge,Comparator.reverseOrder())
  1. 第一种写法会逆序之前的全部排序规则,如果思路不清晰容易出错,如果要排序的全部字段都按照降序,推荐使用这个,直接在最后添加reversed()就好。
  2. 但是如果比较比较复杂,使用Comparator.reverseOrder()比较稳妥一些。

skip

跳过指定个数的Stream中元素,获取后面的元素。

// 跳过前5个元素,返回后面的
List<UmsPermission> skipList = permissionList.stream()
    .skip(5)
    .collect(Collectors.toList());Copy to clipboardErrorCopied

distinct

剔除重复的元素

Collectors

Collectors.toList()
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)
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()
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

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));

略,可以尝试键相同的情况下,不设置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

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()));
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

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等基本类型上,它们可以用来产生类似如下的统计结果。

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 不仅仅是这些,这只是基本的使用


全部代码

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"), "高三"));
        // Collectors.toList()
        List<Student> studentList = students.stream().filter(student -> student.getAge() > 20).collect(Collectors.toList());
        // Collectors.joining()
        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());
        // Collectors.toMap()/Collectors.toConcurrentMap()
        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));
        //Collectors.groupingBy()/Collectors.groupingByConcurrent()
        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()));
        //Collectors.partitioningBy() 分割列表 一个为false 一个为true
        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());
         //这里为了程序正常执行,并没有创建相同的id
        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);
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值