• 使用Java Stream,提取集合中的某一列/按条件过滤集合/求和/最大值/最小值/平均值


    不得不说,使用Java Stream操作集合实在是太好用了,不过最近在观察生产环境错误日志时,发现偶尔会出现以下2个异常:

    1. java.lang.NullPointerException
    2. java.util.NoSuchElementException

    因此本篇博客总结下使用Java Stream的部分场景以及如何避免上述的2个异常:

    1. 提取集合中的某一列(普通提取、去重)
    2. 按条件过滤集合
    3. 求和
    4. 最大值/最小值/平均值

    1. 数据准备

    首先定义下Friend类:

    package com.zwwhnly.springbootaction.model;
    
    import lombok.Data;
    
    import java.math.BigDecimal;
    
    @Data
    public class Friend {
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 年龄
         */
        private Integer age;
    
        /**
         * 身高
         */
        private Long height;
    
        /**
         * 所在城市
         */
        private String city;
    
        /**
         * 体重
         */
        private BigDecimal weight;
    
        public Friend(String name, Integer age, Long height, String city, BigDecimal weight) {
            this.name = name;
            this.age = age;
            this.height = height;
            this.city = city;
            this.weight = weight;
        }
    }
    

    然后初始化以下数据供后面使用:

    public static List<Friend> getFriendList() {
        List<Friend> friendList = new ArrayList<>();
        friendList.add(new Friend("小周", 28, 175L, "郑州", new BigDecimal("101.5")));
        friendList.add(new Friend("小吴", 28, 170L, "洛阳", new BigDecimal("111.5")));
        friendList.add(new Friend("小郑", 29, 176L, "郑州", new BigDecimal("121.5")));
        friendList.add(new Friend("小王", 29, 180L, "北京", new BigDecimal("130")));
        friendList.add(new Friend("小赵", 27, 178L, "苏州", new BigDecimal("140")));
        friendList.add(new Friend("小钱", null, null, "杭州", new BigDecimal("150")));
    
        return friendList;
    }
    

    2. 提取集合中的某一列

    2.1 普通提取

    比如,我们需要提取出所有朋友的姓名,可以使用Stream的map()方法,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    List<String> nameList = friendList.stream().map(Friend::getName).collect(Collectors.toList());
    
    nameList.forEach(name -> System.out.println(name));
    

    输出结果:

    小周

    小吴

    小郑

    小王

    小赵

    2.2 提取后去重

    比如,我们需要提取出所有朋友的年龄,但是需要去重,可以使用Stream的distinct()方法,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    List<Integer> ageList = friendList.stream().map(Friend::getAge).distinct().collect(Collectors.toList());
    
    ageList.forEach(age -> System.out.println(age));
    

    输出结果:

    28

    29

    27

    3. 按条件过滤集合

    比如,我们需要获取所有朋友中年龄在29岁以下,并且身高在170以上的朋友,可以调用filter方法,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    
    List<Friend> youngPeople = friendList.stream()
            .filter(friend -> friend.getAge() != null && friend.getAge() < 29 &&
                    friend.getHeight() != null && friend.getHeight() > 170L)
            .collect(Collectors.toList());
    System.out.println(youngPeople);
    

    输出结果:

    Friend(name=小周, age=28, height=175, city=郑州, weight=101.5)

    Friend(name=小赵, age=27, height=178, city=苏州, weight=140)

    4. 求和

    4.1 Integer,Long,Double

    比如,我们需要计算出所有朋友的年龄之和,可以调用mapToInt方法,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    
    int ageSum = friendList.stream().filter(friend -> friend.getAge() != null).mapToInt(Friend::getAge).sum();
    System.out.println(ageSum);
    

    输出结果:

    141

    注意事项:

    因为我们的age字段定义的是包装类型Integer,但求和之后的返回类型为基本类型int,所以在调用mapToInt方法之前,一定要过滤掉年龄为null的数据,否则分分钟抛异常。

    比如,我们添加一条年龄为null的数据:

    friendList.add(new Friend("小钱",null,178,"杭州"));
    

    然后,我们不过滤null数据,直接调用mapToInt方法,就会抛出java.lang.NullPointerException异常:

    List<Friend> friendList = getFriendList();
    
    int ageSum = friendList.stream().mapToInt(Friend::getAge).sum();
    System.out.println(ageSum);
    

    如果字段类型是Long或者Double,可以调用相应的mapToDoublemapToLong,如下所示:

    4.2 BigDecimal

    和Integer、Long、Double类型不同,如果字段类型是BigDecimal,求和的话需要调用reduce方法,使用方法如下所示:

    List<Friend> friendList = getFriendList();
    
    BigDecimal weightSum = friendList.stream()
            .filter(friend -> friend.getWeight() != null)
            .map(Friend::getWeight)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    System.out.println(weightSum);
    

    输出结果:

    754.5

    注意事项:

    为避免java.lang.NullPointerException异常,上面代码中的.filter(friend -> friend.getWeight() != null)也要记得加。

    5. 最大值/最小值/平均值

    5.1 Integer,Long,Double

    比如,我们需要获取所有朋友中身高的最大值,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    
    long heightMax = friendList.stream()
            .filter(friend -> friend.getHeight() != null)
            .mapToLong(Friend::getHeight)
            .max().orElse(0);
    System.out.println(heightMax);
    

    输出结果:

    180

    注意事项:

    因为max()方法的返回值是OptionalLong类型,所以我们需要继续调用orElse()方法设置个默认值,这里不要直接使用getAsLong()方法,因为当集合为空时,会抛出你肯定遇到过的java.util.NoSuchElementException异常:

    long heightMax = friendList.stream()
            .filter(friend -> friend.getHeight() != null)
            .mapToLong(Friend::getHeight)
            .max().getAsLong();
    

    orElse()源码如下所示:

    public long orElse(long other) {
        return isPresent ? value : other;
    }
    

    getAsLong()源码如下所示:

    public long getAsLong() {
        if (!isPresent) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    

    类似地,获取最小值的代码如下所示:

    List<Friend> friendList = getFriendList();
    
    long heightMin = friendList.stream()
            .filter(friend -> friend.getHeight() != null)
            .mapToLong(Friend::getHeight)
            .min().orElse(0);
    System.out.println(heightMin);
    

    获取平均值的代码如下所示:

    List<Friend> friendList = getFriendList();
    
    double heightAverage = friendList.stream()
        .filter(friend -> friend.getHeight() != null)
        .mapToLong(Friend::getHeight)
        .average().orElse(0D);
    System.out.println(heightAverage);
    

    5.2 BigDecimal

    比如,我们需要获取所有朋友中体重的最大值,实现代码如下所示:

    List<Friend> friendList = getFriendList();
    
    BigDecimal weightMax = friendList.stream()
            .filter(friend -> friend.getWeight() != null)
            .map(Friend::getWeight)
            .max(BigDecimal::compareTo)
            .orElse(BigDecimal.ZERO);
    System.out.println(weightMax);
    

    输出结果:

    150

    注意事项:

    1)为避免出现java.lang.NullPointerException异常,注意过滤体重为null的数据

    2)因为max()方法的返回值为Optional类型,所以我们需要继续调用orElse()方法设置个默认值,这里不要直接使用get()方法,因为当集合为空时,会抛出你肯定遇到过的java.util.NoSuchElementException异常:

    BigDecimal weightMax = friendList.stream()
            .filter(friend -> friend.getWeight() != null)
            .map(Friend::getWeight)
            .max(BigDecimal::compareTo)
            .get();
    

    get()方法源码如下所示:

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    

    类似地,获取最小值的代码如下所示:

    List<Friend> friendList = getFriendList();
    
    BigDecimal weightMax = friendList.stream()
            .filter(friend -> friend.getWeight() != null)
            .map(Friend::getWeight)
            .min(BigDecimal::compareTo)
            .orElse(BigDecimal.ZERO);
    System.out.println(weightMax);
    

    6. 总结

    使用Java Stream操作集合非常便利,但还是容易踩一些坑,比如文中提到的java.lang.NullPointerException异常和java.util.NoSuchElementException异常,所以使用时要多多注意,能不踩坑就不踩坑,就算踩坑,也别多次踩同一个坑。


  • 相关阅读:
    Teamwork——Week4 团队分工和预估项目时间
    Team Homework #3 软件工程在北航——IloveSE
    《DWZ笔记一》<select>动态联动菜单
    asp.net下使用jquery 的ajax+WebService+json 实现无刷新取后台值的实现
    经典SQL语句基础50题
    打垮美国发起的这场经济战争[转自由凤凰论坛]
    为什么要删我贴呢?
    5年,从5元到500万,我的创业经历(转自VFP精英站,未完)
    发现大家对销售感兴趣,再来两则(二)
    你们知道美国是怎么报道别人的灾难吗?
  • 原文地址:https://www.cnblogs.com/zwwhnly/p/13683381.html
Copyright © 2020-2023  润新知