重复操作数组的时候一不小心就炸了

234 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

背景

开发的时候,需要对数据进行不同的操作,特别是对同一组数据进行多次操作时,如果没有注意数据隔离就容易出现bug。

案例

一个数组中有三个人的信息,要重复操作给这三个人编号,把新数据存到新的数组中,然后问题就来了。

@Test
public void test4() {
    Random r = new Random();
    List<Map<String, Object>> l = new ArrayList<>();
    List<Map<String, Object>> l1 = new ArrayList<>();
    Map<String, Object> m = new HashMap<>();
    m.put("name", "张三");
    Map<String, Object> m1 = new HashMap<>();
    m1.put("name", "李四");
    Map<String, Object> m2 = new HashMap<>();
    m2.put("name", "王五");
    l1.add(m);
    l1.add(m1);
    l1.add(m2);
    for (int i = 0; i < 3; i++) {
        for (Map<String, Object> b : l1) {
            int t = r.nextInt(1000);
            b.put("分数", t);

            System.out.println(t);
        }
        l.addAll(l1);
    }

    System.out.println(l);

}

输出结果:

403
134
865
259
882
491
112
950
714
[{分数=112, name=张三}, {分数=950, name=李四}, {分数=714, name=王五}, {分数=112, name=张三}, {分数=950, name=李四}, {分数=714, name=王五}, {分数=112, name=张三}, {分数=950, name=李四}, {分数=714, name=王五}]

尽管每次插入的随机数不同,但是发现结果是重复的,因为循环操作了同一个数组l1,而数组l中保存的是l1的地址(l.addAll(l1);)导致最后的结果是3组相同的数据。所以我们需要新建一个数组l2去接收数组l1的数据,每次操作新数组,实现数据隔离。

方案一:List.addAll()(浅拷贝无效)

List<Map<String, Object>> l2 = new ArrayList<>();
l2.addAll(l1);

方案二:使用List的构造方法(浅拷贝无效)

List<Map<String, Object>> l2 = new ArrayList<>(l1);

两种方案输出结果还是重复的数据

[{分数=447, name=张三}, {分数=806, name=李四}, {分数=212, name=王五}, {分数=447, name=张三}, {分数=806, name=李四}, {分数=212, name=王五}, {分数=447, name=张三}, {分数=806, name=李四}, {分数=212, name=王五}]

打印JSONObject.toJSONString()打印结果会发现还是相同的地址引用

System.out.println(JSONObject.toJSONString(l));

结果:

[{"分数":413,"name":"张三"},{"分数":206,"name":"李四"},{"分数":279,"name":"王五"},{"$ref":"$[0]"},{"$ref":"$[1]"},{"$ref":"$[2]"},
{"$ref":"$[0]"},{"$ref":"$[1]"},{"$ref":"$[2]"}]

 

方案三:数据类型转换(深拷贝)

@Test
    public void test6() {
        Random r = new Random();
        List<Map<String, Object>> l = new ArrayList<>();
        List<Map<String, Object>> l1 = new ArrayList<>();
        Map<String, Object> m = new HashMap<>();
        m.put("a", "1");
        Map<String, Object> m1 = new HashMap<>();
        m1.put("a", "2");
        l1.add(m);
        l1.add(m1);
        for (int i = 0; i < 3; i++) {
            // 方案三,转成String,在转回List
            String str = JSONObject.toJSONString(l1);
            List<Map<String, Object>> l2 = JSONObject.parseObject(str, List.class);
            
            for (Map<String, Object> b : l2) {
                int t = r.nextInt(1000);
                b.put("c", t);

                System.out.println(t);
            }

            l.addAll(l2);
        }

        System.out.println(l);

    }

测试发现数据没有重复,实现数据隔离

941
685
901
129
449
516
[{"a":"1","c":941}, {"a":"2","c":685},  {"a":"1","c":901}, {"a":"2","c":129},  {"a":"1","c":449}, {"a":"2","c":516}]

当然还有其他序列化方式的深度拷贝都能实现数据隔离,之后再补充