zoukankan      html  css  js  c++  java
  • Java8 Streams 让集合操作飞起来

    前言

    接上篇文章 java8 新特性 由于上篇过于庞大,使得重点不够清晰,本篇单独拿出 java8 的 Stream 重点说明 ,并做了点补充。

    基本说明

    • Stream 是基于 java8 的 lambda 表达式的,如果不清楚 lambda 表达式,可以查看我的上篇文章Lambda 表达式和函数式接口快速理解
    • Stream 把要处理的元素看做一种流,流在管道中传输,可以在管道的节点上处理数据,包含过滤,去重,排序,映射,聚合,分组等。
    • Stream 分为中间操作和后期操作,中期操作会形成一个新的 Stream ,但不会马上对数据进行处理,到后期操作时,再遍历整个集合;可以没有中期操作直接后期操作。

    创建流的方式

    • 使用 java.util.Collection 接口的默认方法 stream 或者 parallelStream
    • 使用 java.util.Arrays 的方法 stream 将数组变成流

    中期操作和后期操作

    Stream 分为中间操作和后期操作,中期操作会形成一个新的 Stream ,但不会马上对数据进行处理,到后期操作时,再遍历整个集合;可以没有中期操作直接后期操作。

    中期操作

    • map 和 map 之类的,用于映射一种类型到另一种类型
    • filter 用于过滤掉一些不符合要求的元素
    • distinct 用于排重
    • sorted 用于排序
    • flatMap 用于将流扁平化

    后期操作

    forEach,collect,reduce,anyMatch,allMatch,noneMatch,findFirst 等;

    其中属 collect 最为常用,还有一个专门用于 collect 的 Collectors 类,可以用于将集合转成 List,Set,Map 等

    代码示例

    数据准备

    1. 准备一个用于下面例子测试的对象
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Vehicle {
        //车架号
        private String vin;
        // 车主手机号
        private String phone;
        // 车主姓名
        private String name;
        // 所属车租车公司
        private Integer companyId;
        // 个人评分
        private Double score;
        //安装的设备列表imei,使用逗号分隔
        private String deviceNos;
    }
    
    1. 准备一些车辆数据
    static List<Vehicle> vehicles = new ArrayList<>();
    
    @Before
    public void init(){
        List<String> imeis = new ArrayList<>();
        for (int i = 0; i <5 ; i++) {
            List<String> singleVehicleDevices = new ArrayList<>();
            for (int j = 0; j < 3; j++) {
                String imei = RandomStringUtils.randomAlphanumeric(15);
                singleVehicleDevices.add(imei);
            }
            imeis.add(StringUtils.join(singleVehicleDevices,','));
        }
        vehicles.add(new Vehicle("KPTSOA1K67P081452","17620411498","9420",1,4.5,imeis.get(0)));
        vehicles.add(new Vehicle("KPTCOB1K18P057071","15073030945","张玲",2,1.4,imeis.get(1)));
        vehicles.add(new Vehicle("KPTS0A1K87P080237","19645871598","sanri1993",1,3.0,imeis.get(2)));
        vehicles.add(new Vehicle("KNAJC526975740490","15879146974","李种",1,3.9,imeis.get(3)));
        vehicles.add(new Vehicle("KNAJC521395884849","13520184976","袁绍",2,4.9,imeis.get(4)));
    }
    

    forEach 遍历Collection 数据

    vehicles.forEach(vehicle -> System.out.println(vehicle));
    
    //这样就可以遍历打印
    vehicles.forEach(System.out::println);
    

    forEach 遍历 Map 数据

    Map<String,Integer> map = new HashMap<>();
    map.put("a",1);map.put("b",2);map.put("c",3);
    
    map.forEach((k,v) -> System.out.println("key:"+k+",value:"+v));
    

    filter 数据过滤

    // 去掉评分为 3 分以下的车
    List<Vehicle> collect = vehicles.stream().filter(vehicle -> vehicle.getScore() >= 3).collect(Collectors.toList());
    

    map 对象映射

    对一个 List<Object> 大部分情况下,我们只需要列表中的某一列,或者需要把里面的每一个对象转换成其它的对象,这时候可以使用 map 映射,示例:

    // 取出所有的车架号列表
     List<String> vins = vehicles.stream().map(Vehicle::getVin).collect(Collectors.toList());
    

    groupBy 按照某个属性进行分组

    // 按照公司 Id 进行分组
    Map<Integer, List<Vehicle>> companyVehicles = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId));
    
    // 按照公司分组求司机打分和
    Map<Integer, Double> collect = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId, Collectors.summingDouble(Vehicle::getScore)));
    

    sort 按照某个属性排序 ,及多列排序

    // 单列排序 
    vehicles.sort((v1,v2) -> v2.getScore().compareTo(v1.getScore()));
    
    // 或使用 Comparator 类来构建比较器,流处理不会改变原列表,需要接收返回值才能得到预期结果
     List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()).collect(Collectors.toList());
    
    // 多列排序,score 降序,companyId 升序排列
    List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()
                    .thenComparing(Comparator.comparing(Vehicle::getCompanyId)))
                    .collect(Collectors.toList());
    

    flatMap 扁平化数据处理

    // 查出所有车绑定的所有设备
    List<String> collect = vehicles.stream().map(vehicle -> {
        String deviceNos = vehicle.getDeviceNos();
        return StringUtils.split(deviceNos,',');
    }).flatMap(Arrays::stream).collect(Collectors.toList());
    

    flatMap 很适合 List<List>List<object []> 这种结构,可以当成一个列表来处理;像上面的设备列表,在数据库中存储的结构就是以逗号分隔的数据,而车辆列表又是一个列表数据。

    将 List 数据转成 Map

    // 将 List 转成 Map ; key(vin) == > Vehicle
    Map<String, Vehicle> vinVehicles = vehicles.stream().collect(Collectors.toMap(Vehicle::getVin, vehicle -> vehicle));
    

    mapReduce 数据处理

    // 对所有司机的总分求和
    Double reduce = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);
    

    求百分比

    // 总的分值
    Double totalScore = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);
    
    // 查看每一个司机占的分值比重
    List<String> collect = vehicles.stream()
        .mapToDouble(vehicle -> vehicle.getScore() / totalScore)
        .mapToLong(weight -> (long) (weight * 100))
        .mapToObj(percentage -> percentage + "%")
        .collect(Collectors.toList());
    

    anyMatch/allMatch/noneMatch 匹配操作

    • anyMatch 只要有元素匹配,即返回真
    • allMatch 要求所有的元素都匹配
    • noneMatch 要求没有一个元素匹配
    // 检查是否有姓李的司机 true
    boolean anyMatch = vehicles.stream().anyMatch(vehicle -> vehicle.getName().startsWith("李"));
    
    // 检查是否所有司机的评分都大于 3 分 false
    boolean allMatch = vehicles.stream().allMatch(vehicle -> vehicle.getScore() > 3);
    
    // 检查是否有 3 公司的特务 true
    boolean noneMatch = vehicles.stream().noneMatch(vehicle -> vehicle.getCompanyId() == 3);
    

    一点小推广

    创作不易,希望可以支持下我的开源软件,及我的小工具,欢迎来 gitee 点星,fork ,提 bug 。

    Excel 通用导入导出,支持 Excel 公式
    博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
    gitee:https://gitee.com/sanri/sanri-excel-poi

    使用模板代码 ,从数据库生成代码 ,及一些项目中经常可以用到的小工具
    博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
    gitee:https://gitee.com/sanri/sanri-tools-maven

  • 相关阅读:
    centos8 将SSSD配置为使用LDAP并要求TLS身份验证
    Centos8 搭建 kafka2.8 .net5 简单使用kafka
    .net core 3.1 ActionFilter 拦截器 偶然 OnActionExecuting 中HttpContext.Session.Id 为空字符串 的问题
    Springboot根据不同环境加载对应的配置
    VMware Workstation12 安装 Centos8.3
    .net core json配置文件小结
    springboot mybatisplus createtime和updatetime自动填充
    .net core autofac依赖注入简洁版
    .Net Core 使用 redis 存储 session
    .Net Core 接入 RocketMQ
  • 原文地址:https://www.cnblogs.com/sanri1993/p/11580929.html
Copyright © 2011-2022 走看看