You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java循环创建嵌套Map时键始终为0,仅保留最后一条记录问题

问题分析与解决:嵌套Map存入时的引用覆盖问题

你的问题场景

你尝试将Map<String, Object>存入Map<String, Map<String, Object>>,但遇到了诡异的覆盖问题:循环执行5次,每次createmap都生成了新记录,但最终result里始终只有键为0的条目,值还被最后一次的内容覆盖。先看看你的代码和打印输出:

你的代码

Map<String, Object> createmap = new HashMap<String, Object>();
Map<String, Object> result = new HashMap<String, Object>();
for (int j=0 ; j<jsonarr2.size(); j++ ) {
    JSONObject jsonobj2 = (JSONObject) jsonarr2.get(j);
    Iterator<String> key2 = jsonobj2.keySet().iterator();
    while (key2.hasNext()) {
        String k2 = key2.next();
        createmap.put(k2, jsonobj2.get(k2));
    }
    System.out.println("Print J:" + j + " CREATE MAP:" + createmap);
    result.put(Integer.toString(j),createmap);
    System.out.println("result :" + result);
}

循环打印输出

Print J:0 CREATE MAP:{vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0001","timestamp":1548643752}}}}
result : {0={vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0001","timestamp":1548643752}}}}}
Print J:1 CREATE MAP:{vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0002","timestamp":1548643752}}}}
result : {0={vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0002","timestamp":1548643752}}}}}
Print J:2 CREATE MAP:{vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0003","timestamp":1548643752}}}}
result : {0={vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0003","timestamp":1548643752}}}}}
Print J:3 CREATE MAP:{vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0004","timestamp":1548643752}}}}
result : {0={vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0004","timestamp":1548643752}}}}}
Print J:4 CREATE MAP:{vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0005","timestamp":1548643752}}}}
result : {0={vsan_name={"value":{"0":{"sequence":0,"value":"VSAN0005","timestamp":1548643752}}}}}

最终result仅保留了最后一条记录,键始终显示为0,这到底是哪里出问题了?

问题根源

这是Java引用类型的经典坑——你只创建了一个createmap对象

在循环外面初始化的createmap,本质上是一个指向HashMap内存地址的引用。每次循环你只是修改这个对象里的内容,然后把同一个引用存入result中。因为Java中对象是按引用传递的,result.put(...)存的不是当前createmap的副本,而是它的内存地址。所以每次你修改createmap的内容时,result里所有指向它的条目都会同步变化。

至于为什么看起来键始终是0?其实不是的,你每次循环都往result里放了不同的键(0、1、2、3、4),但这些键对应的都是同一个createmap对象。当你打印result时,控制台显示的是这个对象当前的状态,而每次循环你都覆盖了createmap里的vsan_name值,最后所有条目指向的都是最后一次修改后的状态,看起来就像只有键0存在,但实际上result里有5个键,只是它们的value全是同一个对象的引用。

修复方案

解决方法超简单——createmap的创建移到循环内部,这样每次循环都会生成一个全新的HashMap对象,每个对象的内容都是独立的,存入result后不会被后续循环修改:

Map<String, Object> result = new HashMap<String, Object>();
for (int j=0 ; j<jsonarr2.size(); j++ ) {
    // 每次循环新建一个独立的createmap
    Map<String, Object> createmap = new HashMap<String, Object>();
    JSONObject jsonobj2 = (JSONObject) jsonarr2.get(j);
    Iterator<String> key2 = jsonobj2.keySet().iterator();
    while (key2.hasNext()) {
        String k2 = key2.next();
        createmap.put(k2, jsonobj2.get(k2));
    }
    System.out.println("Print J:" + j + " CREATE MAP:" + createmap);
    result.put(Integer.toString(j),createmap);
    System.out.println("result :" + result);
}

修改后,每次循环都会生成独立的createmap,存入result的是不同对象的引用,后续修改不会影响之前的条目,最终result里会有5个完整的键值对。

另外,还可以简化遍历JSONObject的代码,用增强for循环代替迭代器,更简洁易读:

Map<String, Object> result = new HashMap<>();
for (int j = 0; j < jsonarr2.size(); j++) {
    Map<String, Object> createmap = new HashMap<>();
    JSONObject jsonobj2 = (JSONObject) jsonarr2.get(j);
    // 增强for循环遍历所有key
    for (String k2 : jsonobj2.keySet()) {
        createmap.put(k2, jsonobj2.get(k2));
    }
    result.put(String.valueOf(j), createmap);
}

内容的提问来源于stack exchange,提问作者user1882624

火山引擎 最新活动