List.add 方法添加元素时只会添加最后一条元素的问题与解决

List.add 方法添加元素时只会添加最后一条元素的分析解决


前言

在之前编写业务代码时, 遇到了一个比较神奇的现象, 如标题中描述的那样:
在对list 集合使用 add/set 方法并且遍历的去添加对象时, 只会添加最后一个元素的问题 .
下面就进行简单的分析,

一、问题描述

现有一个需求:
在请求时携带一个map, 该map里面的key有几个. 那么, 在返回结果集 map中就需要将对应的 key 和 value 捞出来一起展示,
如果结果不止一个map, 则将这些map放入一个list中

  1. 模拟代码片如下
            //模拟请求
            Map<String, Object> requestMap = new HashMap<>();
            requestMap.put("id", "");
            requestMap.put("passwd", "");
            requestMap.put("userName", "");
            //模拟结果集
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("id", "1");
            resultMap.put("passwd", "odd taxi");
            resultMap.put("userName", "小早川");
            Map<String, Object> resultMap2 = new HashMap<>();
            resultMap2.put("id", "2");
            resultMap2.put("passwd", "Brazilian war dance");
            resultMap2.put("userName", "白川");
            List<Map<String, Object>> resultListMap = new ArrayList<>();
            resultListMap.add(resultMap);
            resultListMap.add(resultMap2);
            //模拟响应
            List<Map<String, Object>> responseListMap = new ArrayList<>();
            Map<String, Object> responseMap = new HashMap<>();
            // 实现: 根据请求map中的 key, 返回所有结果集中含有请求map所带k的list.map集合
            for (Map<String, Object> resMap : resultListMap) {
                requestMap.forEach((k, v) -> {
                    responseMap.put(k, resMap.get(k));
                });
                responseListMap.add(responseMap);
            }
            System.out.println("responseListMap = " + responseListMap);
    
  2. 结果演示
    可以看到, 在上面结果集 resultListMap遍历过程中, 我们根据请求 requestMap的key 去结果集中寻找key对应的value
    然后放入 responseMap, 因为结果集map 不止一个, 所以将其放入list 中.
    但是, 最后返回的list 中只有两条相同的数据, 而且都是最后插入的一条数据

在这里插入图片描述

二、原因分析

1.简化分析

  1. 为了将原来的问题简化, 我们可以在把问题简化下, 下面是简化之后的代码
        List<User> userList = new ArrayList<>(2);
        User u1 = new User();
        for (int i = 0; i < 5; i++) {
            u1.setId(i);
            u1.setPwd("odd taxi"+ i);
            u1.setUsername("小早川"+ i);
            userList.add(u1);
        }

        for (User user : userList) {
            System.out.println("user = " + user);
        }
  1. 由下图可知
    list.add 通过遍历去调用, 又是只加入最后一条元素, 而且遍历几遍就有几条相同的元素
    在这里插入图片描述

  2. 对上述代码进行断点调试
    由我们对每次循环对象属性和 userList 中属性对比可知:
    在每次循环结束后, userList 中所有的数据都会变成最后一次遍历数据*遍历次数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

结合上面问题和调试结果, 以及资料可知:
1. List 中的 add, set 方法在添加对象(Object) 或者是集合(Collection)时, 添加的是对对象的引用
因此, 如果在循环外声明要保存的对象或集合, 但是却在循环内赋值的话, 就会导致上述问题
2. 因为在循环外对象或者集合只声明了一次, 因此无论如何赋值, 只会保存最后一次赋值.
在循环内 list.add 方法添加的实际上只相当于对最后一次插入的对象或者集合的引用

  1. 基于上述分析, 我们先修改简化版代码: 只需将对象初始化放在循环内即可
        List<User> userList = new ArrayList<>(2);
        User u1 ;
        for (int i = 0; i < 5; i++) {
            u1 = new User();
            u1.setId(i);
            u1.setPwd("odd taxi"+ i);
            u1.setUsername("小早川"+ i);
            userList.add(u1);
        }

        for (User user : userList) {
            System.out.println("user = " + user);
        }

2.回归本题

根据上述思路我们去解决实际问题, 对比下修改前和修改后的代码

  1. 修改前
		//---------------------修改前---------------------
        //模拟请求
        Map<String, Object> requestMap = new HashMap<>();
        requestMap.put("id", "");
        requestMap.put("passwd", "");
        requestMap.put("userName", "");
        //模拟结果集
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("id", "1");
        resultMap.put("passwd", "odd taxi");
        resultMap.put("userName", "小早川");
        Map<String, Object> resultMap2 = new HashMap<>();
        resultMap2.put("id", "2");
        resultMap2.put("passwd", "Brazilian war dance");
        resultMap2.put("userName", "白川");
        List<Map<String, Object>> resultListMap = new ArrayList<>();
        resultListMap.add(resultMap);
        resultListMap.add(resultMap2);
        //模拟响应
        List<Map<String, Object>> responseListMap = new ArrayList<>();
        Map<String, Object> responseMap = new HashMap<>();
        // 实现: 根据请求map中的 key, 返回所有结果集中含有请求map所带k的list.map集合
        for (Map<String, Object> resMap : resultListMap) {
            requestMap.forEach((k, v) -> {
                responseMap.put(k, resMap.get(k));
            });
            responseListMap.add(responseMap);
        }
        System.out.println("responseListMap = " + responseListMap);
  1. 修改后
        //---------------------修改后---------------------
        //模拟请求
        Map<String, Object> requestMap = new HashMap<>();
        requestMap.put("id", "");
        requestMap.put("passwd", "");
        requestMap.put("userName", "");
        //模拟结果集
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("id", "1");
        resultMap.put("passwd", "odd taxi");
        resultMap.put("userName", "小早川");
        Map<String, Object> resultMap2 = new HashMap<>();
        resultMap2.put("id", "2");
        resultMap2.put("passwd", "Brazilian war dance");
        resultMap2.put("userName", "白川");
        List<Map<String, Object>> resultListMap = new ArrayList<>();
        resultListMap.add(resultMap);
        resultListMap.add(resultMap2);
        //模拟响应
        List<Map<String, Object>> responseListMap = new ArrayList<>();
        // 实现: 根据请求map中的 key, 返回所有结果集中含有请求map所带k的list.map集合
        for (Map<String, Object> resMap : resultListMap) {
            // 问题修改: 将 list 需要 add 的对象/集合 放到循环内进行初始化!!!
            Map<String, Object> responseMap = new HashMap<>();
            requestMap.forEach((k, v) -> {
                responseMap.put(k, resMap.get(k));
            });
            responseListMap.add(responseMap);
        }
        System.out.println("responseListMap = " + responseListMap);
  1. 可以看出仅仅是将list 中需要 add 的集合 responseMap 的初始化放到循环内, 问题就得到解决, 如下图所示
    在这里插入图片描述

总结

1. List 中的 add, set 方法在添加对象(Object) 或者是集合(Collection)时, 添加的是对对象的引用
2. 在循环外声明对象或集合, 在循环内使用list.add 就会导致list 中引用的数据地址全部都是最后一次添加的元素地址
如果想要避免. 只需要将被add的对象/集合在循环内初始化即可


参考: https://blog.csdn.net/ww77889126/article/details/84952631

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时间静止不是简史

感谢你的肯定, 我将继续努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值