美文网首页JAVA开发工作总结
JAVA 8 Stream操作List和Map以及复合拼装对象

JAVA 8 Stream操作List和Map以及复合拼装对象

作者: 暴走的狐狸 | 来源:发表于2023-02-23 17:37 被阅读0次

    1 程序员对什么数据类型操作做多?
    毋庸置疑,那就是集合类的数据类型。不管是LIST,MAP,SET或者是python的字典。
    2 List的相关操作java流操作:
    场景一 java8的LIST和map进行按某个条件分组,然后根据特定字段去重,最后统计去重后每组的个数

    import java.util.*;
    
    public class GroupByExample {
        public static void main(String[] args) {
            List<Person> list = new ArrayList<>();
            list.add(new Person("John", "Male", 20));
            list.add(new Person("Alice", "Female", 18));
            list.add(new Person("Bob", "Male", 20));
            list.add(new Person("Carol", "Female", 18));
            list.add(new Person("David", "Male", 20));
     
           // 根据某个字段分组,并返回Map<key,List<Object>>的数据格式
           // Group by gender 
            Map<String, List<Person>> genderGroup = list.stream().collect(
                Collectors.groupingBy(Person::getGender));
     
          // 根据某个字段分组,并返回Map<key,Integer>的计数格式,也就是拿到这个key有多少条聚合的数据。
          // Count the number of persons in each group
            Map<String, Long> countByGender = list.stream().collect(
                Collectors.groupingBy(Person::getGender, Collectors.counting()));
     
            //多个字段进行分组,并返回Map<key,Integer>的计数格式,也就是拿多个字段进行组合分组
            // Group by gender and age
            Map<String, Map<Integer, List<Person>>> ageGroup = list.stream().collect(
                Collectors.groupingBy(Person::getGender,
                    Collectors.groupingBy(Person::getAge)));
     
            // Count the number of persons in each gender and age group
            Map<String, Map<Integer, Long>> countByGenderAndAge = list.stream().collect(
                Collectors.groupingBy(Person::getGender,
                    Collectors.groupingBy(Person::getAge, Collectors.counting())));
            //按性别分组,然后根据name去重
            // Group by gender and remove duplicates based on name
            Map<String, List<Person>> distinctNameByGender = list.stream().collect(
                    Collectors.groupingBy(Person::getGender,
                            Collectors.collectingAndThen(
                                    toCollection(() ->
                                            new TreeSet<>(Comparator.comparing(Person::getName))
                                    ),ArrayList::new)
                    ));
        }
    }
    

    POJO对象

    class Person {
        private String name;
        private String gender;
        private int age;
     
        public Person(String name, String gender, int age) {
            this.name = name;
            this.gender = gender;
            this.age = age;
        }
       //  省略setter and getter
    }
    

    JAVA LIST 多个字段 group by的时候,我一般喜欢封装一个方法,加一个连接符来处理。比如我有一个对象,叫
    ActualSortingLog,当前分拣日志, 我希望根据sortingTime和PipeLine进行分组。那就可以创建一个叫
    fetchGroupKey的方法

      /**
         * 
         * 根据sortingTime和PipeLine进行分组
         * @param actualSortingLog
         * @return
         */
        private String fetchGroupKey(ActualSortingLog actualSortingLog) {
           return actualSortingLog.getSortingTime() +"#"+ user.getPipeLine();
        }
    

    这样好处是解耦,也方便扩展,代码也可读。进一步,如果希望对字段做一些处理,再分组,也就简单很多。比如
    这边进一步,希望按分钟和pipeLine字段的前3位聚合,同时时间格式变为yyyyMMddHHmm这种,则代码如下:

      /**
         * 按分钟线加pipeline前三位进行聚合
         *
         * @param actualSortingInfoDTO
         * @return
         */
        private String makeGroupKeyMinuteWithDeviceCode(ActualSortingInfoDTO actualSortingInfoDTO) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
            return dateTimeFormatter.format(LocalDateTimeUtils.longToLocalTime(actualSortingInfoDTO.getSortTime())) + SEPARATOR +
                    filterDeviceCode(actualSortingInfoDTO.getPipeline());
        }
    

    当然这里我又封装了时间工具LocalDateTimeUtils类和设备编码切割类filterDeviceCode,是因为这些细碎的逻辑在后续的切割和分组中会经常用到。分开也有利于测试和管理。这里就不展开讲。

    再来说一说List的去重逻辑,首先是简单去重

    @Test
        @DisplayName("list去重测试")
        void testDuplicate() {
            ActualSortingInfoDTO mockData = mockData("001223", "1", 1677037037000L);
            ActualSortingInfoDTO mockData2 = mockData("002331", "2", 1677037037000L);
            // 模拟一个重复的sortingId,应该会去重
            ActualSortingInfoDTO duplicateId = mockData("002331", "2", 1677037037001L);
            List<ActualSortingInfoDTO> list = new ArrayList<>();
            list.add(mockData);
            list.add(mockData2);
            list.add(duplicateId);
    
            List<ActualSortingInfoDTO> distinctList = list.stream().collect(Collectors.collectingAndThen(
                    Collectors.toCollection(simpleTreeSetSupplier()),
                    ArrayList::new));
            log.info("简单去重的数据" + distinctList);
    
            // 复杂逻辑去重,比如我希望根据pipeLine的前三位的值是001来去重
            List<ActualSortingInfoDTO> complexDistinctList = list.stream().collect(Collectors.collectingAndThen(
               Collectors.toCollection(distinctPipeLine()),ArrayList::new
            ));
            log.info("复杂去重的数据" + complexDistinctList);
    
            // list to map,复杂逻辑去重,比如我希望根据pipeLine的前三位的值是001和sortingId不等于2来去重,应该是保留第一条的数据mockData
            Map<String, List<ActualSortingInfoDTO>> distinctMap = ListStreamUtil.group(complexDistinctList,this::makeGroupKeyMinuteWithDeviceCode);
            Assertions.assertEquals(
                    distinctMap.get(makeGroupKeyMinuteWithDeviceCode(mockData)).get(0).getPipeline(),"001223");
            Assertions.assertEquals(
                    distinctMap.get(makeGroupKeyMinuteWithDeviceCode(mockData)).get(0).getSortingId(),"1");
            distinctMap.forEach((k, v) -> log.info("分组后:" + k + " " + v));
        }
    

    这里也建议对复杂的去重方法进行封装,比如我这边封装了一个方法,叫distinctPipeLine,后续就可以自定义各种去重逻辑了。
    自定义去重方法如下:

    private Supplier<TreeSet<ActualSortingInfoDTO>> distinctPipeLine() {
            return () -> new TreeSet<>(
                    Comparator.comparing(actualSortingInfoDTO ->
                    actualSortingInfoDTO.getPipeline().equals("001")
                            && !Objects.equals(actualSortingInfoDTO.getSortingId(), "2")));
        }
    

    相关文章

      网友评论

        本文标题:JAVA 8 Stream操作List和Map以及复合拼装对象

        本文链接:https://www.haomeiwen.com/subject/rnbqkdtx.html