lambda表达式 —— 过滤再排序未生效问题排查

背景

项目中有个场景,需要将一个列表先按要求过滤,再根据某字段排序。图方便使用lambda表达式修改原列表将过滤和排序逻辑写到一行,打印输出列表发现过滤未生效。

代码示例:

java 复制代码
        List<Long> productIdList = Arrays.asList(1L);
        List<ProductDTO> productDTOList = new ArrayList<>();
        ProductDTO r1 = new ProductDTO();
        r1.setProductId(4L);
        r1.setPrice(100);
        productDTOList.add(r1);

        ProductDTO r2 = new ProductDTO();
        r2.setProductId(1L);
        r2.setPrice(20);
        productDTOList.add(r2);

        ProductDTO r3 = new ProductDTO();
        r3.setProductId(1L);
        r3.setPrice(100);
        productDTOList.add(r3);

        ProductDTO r4 = new ProductDTO();
        r4.setProductId(2L);
        r4.setPrice(150);
        productDTOList.add(r4);


        Map<Long, List<ProductDTO>> map = new HashMap<>();
        map.put(111L, productDTOList);

第一种 过滤+排序

java 复制代码
      map.forEach((infoId, productList) -> {
            productList.stream().filter(e -> productIdList.contains(e.getProductId())).collect(Collectors.toList()).sort(Comparator.comparing(ProductDTO::getPrice).reversed());

            System.out.println("=================" + productList);
        });

输出结果:

bash 复制代码
=================[ProductDTO(productId=4, price=100, name=null), ProductDTO(productId=1, price=20, name=null), ProductDTO(productId=1, price=100, name=null), ProductDTO(productId=2, price=150, name=null)]

第二种 过滤排序拆两行

java 复制代码
      map.forEach((infoId, productList) -> {
            productList = productList.stream().filter(e -> productIdList.contains(e.getProductId())).collect(Collectors.toList());
            productList.sort(Comparator.comparing(ProductDTO::getPrice).reversed());

            System.out.println("=================" + productList);
        });

输出结果:

bash 复制代码
=================[ProductDTO(productId=1, price=100, name=null), ProductDTO(productId=1, price=20, name=null)]

从上面示例可以看出第二种是我们需要的,第一种过滤没生效。

分析

针对下面这行代码,分析其运行过程。

java 复制代码
productList.stream().filter(e -> productIdList.contains(e.getProductId())).collect(Collectors.toList()).sort(Comparator.comparing(ProductDTO::getPrice).reversed());
  • stream():
    productList被转换成一个Stream,以声明性方式处理数据集合;
  • filter(e -> productIdList.contains(e.getProductId())):
    Stream上的filter方法被调用,该方法接受一个返回boolean值的函数作为参数。它检查productList中的每一个元素e的productId是否包含在productIdList中,只有满足条件的元素会被保留在Stream中。
  • collect(Collectors.toList()):
    经过filter操作后,Stream中的元素被收集到一个新的List中。
  • sort(Comparator.comparing(ProductDTO::getPrice).reversed()):
    新生成的List被排序 。sort方法接受一个Comparator作为参数,用于定义排序规则。在这里,Comparator.comparing(ProductDTO::getPrice)创建了一个Comparator,它根据ProductDTO对象的getPrice方法返回的价格值进行排序。.reversed()表示降序排列。

综上,List 接口的 sort 方法使用了自然排序或者通过提供的 Comparator 进行排序,是对原始表进行排序,不会创建一个新的已排序的列表。在上述示例中,对collect(Collectors.toList())后生成的新列表进行排序,但新列表并未输出,对原列表productList无影响,既未过滤也未排序。

所以,上面第二种方案是先过滤再排序最后生成新列表返回,只不过原列表productList变量不再使用,指向新列表。也可以使用下面的写法。

java 复制代码
List<ProductDTO> list = productList.stream().filter(e -> productIdList.contains(e.getProductId())).sorted((Comparator.comparing(ProductDTO::getPrice).reversed())).collect(Collectors.toList());

此时list和原有productList为两个不同的列表,输出结果也是我们需要的。

另外productList = productList.stream()...collect(Collectors.toList()); 这种写法将新生成的列表赋值给了productList变量,productList指向了一个新的列表,但不会改变productList内容。如果原始的productList列表没有其他引用指向它,被其他使用,之后会被垃圾回收器回收。

原变量不再使用可以这么写,但会使代码不够清晰甚至引用出现失误,可以创建一个新的变量来存储新列表。

相关推荐
秋饼11 分钟前
MySQL 分库分表完全指南:ShardingSphere-JDBC 实战、分片键选型、场景边界、20 个生产坑与 30 道面试点
java·mysql·面试·shardingsphere·分库分表·后端架构
郭小李16 分钟前
spring 的 @CookieValue 注解 用法
java·后端·spring
天远Date Lab25 分钟前
零信任架构实战:基于天远企业股权变更构建自动化招投标供应商股东穿透审查网关
java·人工智能·架构·自动化
god_cvz41 分钟前
麒麟 V10(ARM64 / aarch64)源码编译 3dtiles 全步骤
后端·gis
武子康43 分钟前
批量设备任务显示 COMPLETED,为什么还有设备失败?
人工智能·后端·agent
weixin_399380691 小时前
TongWeb7.0.4.9M11如何接入到idea(by lqw)
java·ide·intellij-idea
paopaokaka_luck1 小时前
社区服务平台(加权协同过滤推荐、Jieba中文词频分析、WebSocket实时聊天、求助处理闭环、 活动积分闭环、ECharts多维分析)
java·前端·spring boot·mysql·echarts·mybatis
泡海椒1 小时前
折线图Y轴刻度悬空之谜-信用评级趋势
后端
.Bryce.1 小时前
T2-LLM评测不等于Agent评测
android·java·人工智能·后端·gpt·agent·1024程序员节
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 用户体系集成:查询用户环信账号密码
java·交友