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

  • 相关阅读:
    (11)模糊图片操作---均值模糊
    (10)绘制形状和文字
    (9)调整图像的亮度和对比度
    (8)图像混合
    (7)opencv图片内部的基本处理
    C#中的线程池使用(二)
    C#中的线程池使用(一)
    C#中线程的委托
    为PyCharm自动配置作者信息
    为PyCharm配置QT
  • 原文地址:https://www.cnblogs.com/sanri1993/p/11580929.html
Copyright © 2011-2022 走看看