昨天周五,在开发某个接口时遇到了一个非常坑的问题(当然是因为自己基础不够扎实),没有注意到JSONArray和JSONObject深拷贝与浅拷贝的问题,在坑了呆了一下午才出来,心态濒临崩溃……,如果你也遇到了我下面提到的问题,请往后看,我会奉上几种解决方法。
环境:我们在后台定义了一个公共ConcurrentHashMap
需求就是:对于Map中某张表的每条记录的某字段进行条件拼装,然后返回给前台。就这么简单的一个需求差点把我心态搞爆炸。我从Map中取出JSONArray数据后,遍历对字段进行修改,然后发现Map中的原始数据也跟着改变了,天哪,当时觉得可诡异了
IDEA中准备测试环境我就不贴出来了,不是本文的重点,主要就是maven中导入fastjson的依赖坐标,乐意用Junit的导入依赖,不乐意用的就用psvm生成main方法测试就行。测试数据准备如下:
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.junit.Test;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @version V1.0
* @Title:
* @Package PACKAGE_NAME
* @Description: TODO
* @author: zongshaofeng
* @date 2021/5/22
*/
public class TestFastjson {
private final static Map<String, JSONArray> map = new ConcurrentHashMap<String, JSONArray>();
static {
//初始化map
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < 3; i++) {
JSONObject user = new JSONObject();
user.put("name", "test" + i);
user.put("age", 18 + i);
user.put("birthday", new Date());
user.put("content", "content"+i);
jsonArray.add(user);
}
map.put("userTable", jsonArray);
}
@Test
public void test(){
System.out.println(map);
}
}
打印Map结果如下:
{userTable=[{"birthday":1621678381131,"name":"test0","age":18,"content":"content0"},{"birthday":1621678381131,"name":"test1","age":19,"content":"content1"},{"birthday":1621678381131,"name":"test2","age":20,"content":"content2"}]}
@Test
public void recurringProblem() {
//首先从Map取出JSONArray
JSONArray userDataJSONArray = map.get("userTable");
for (int i = 0; i < userDataJSONArray.size(); i++) {
JSONObject userJSONObject = userDataJSONArray.getJSONObject(i);
String content = userJSONObject.getString("content");
content += "+++模拟拼接content字段内容";
userJSONObject.put("content",content);
}
System.out.println("打印userDataJSONArray对象"+userDataJSONArray);
System.out.println("打印Map中的userTable对象"+map.get("userTable"));
}
结果:
天哪!!!!我都已经将JSONArray取出来了,为啥我改了JSONArray元素的字段值,Map中的数据也跟着变了?
大家是不是第一时间就想到,那我new一个新的JSONArray或者JSONObject是不是就可以了,毕竟我们在学习Java基础时,很确定new出来的两个看起来一模一样的对象equals不相等。我一开始下意识的也是这么想的,并付诸行动。如下所示:
@Test
public void recurringProblem() {
//首先从Map取出JSONArray
JSONArray tempJSONArray = map.get("userTable");
JSONArray userDataJSONArray=new JSONArray(tempJSONArray);
for (int i = 0; i < userDataJSONArray.size(); i++) {
JSONObject userJSONObject = userDataJSONArray.getJSONObject(i);
String content = userJSONObject.getString("content");
content += "+++模拟拼接content字段内容";
userJSONObject.put("content",content);
}
System.out.println("打印userDataJSONArray对象"+userDataJSONArray);
System.out.println("打印Map中的userTable对象"+map.get("userTable"));
}
结果:
从运行结果看,并没有解决问题。我再贴出两个对象的地址看看:
两个对象的内存地址就是不一样的,但是问题依旧存在,那问题出在哪里呢?
这个时候,源码伺候啊……让我们看一下JSONArray和JSONObject源码是怎么样的?
是不是有种恍然大悟的感觉?属性里面都加了final字段,由final关键字的特性:“如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改”
结论:new JSONArray(List list)和new JSONObject(Map
写到这里,我还是能感觉到昨天下午面对这个问题我那痛苦的心情……
这个问题的解决其实有好几种方法,下面我给出两种,抛砖引玉,大家可以继续深入学习。
我们对测试方法进行改造,使用addAll方法复制JSONArray,然后运行看结果
@Test
public void recurringProblem() {
//首先从Map取出JSONArray
JSONArray jsonArray = map.get("userTable");
JSONArray userDataJSONArray = new JSONArray();
userDataJSONArray.addAll(jsonArray);
for (int i = 0; i < userDataJSONArray.size(); i++) {
JSONObject userJSONObject = userDataJSONArray.getJSONObject(i);
String content = userJSONObject.getString("content");
content += "+++模拟拼接content字段内容";
userJSONObject.put("content", content);
}
System.out.println("打印userDataJSONArray对象" + userDataJSONArray);
System.out.println("打印Map中的userTable对象" + map.get("userTable"));
}
发现了吗?为啥结果还是有问题呢?这就又涉及到上述的原理了,JSONArray现在是深度拷贝出来了,对复制出来的JSONArray对象进行添加或者删除JSONObject对象完全不会影响源对象的数据。
但是内部的JSONObject还是final修饰的呀,改动还是会影响的,因为堆里就那一份数据,因此这还没结束,还需要使用putAll()方法对JSONObject对象进行深度拷贝
继续改造:
@Test
public void recurringProblem() {
//首先从Map取出JSONArray
JSONArray jsonArray = map.get("userTable");
JSONArray userDataJSONArray = new JSONArray();
userDataJSONArray.addAll(jsonArray);
for (int i = 0; i < userDataJSONArray.size(); i++) {
JSONObject jsonObject = userDataJSONArray.getJSONObject(i);
JSONObject userJSONObject=new JSONObject();
userJSONObject.putAll(jsonObject);
String content = userJSONObject.getString("content");
content += "+++模拟拼接content字段内容";
userJSONObject.put("content", content);
userDataJSONArray.set(i,userJSONObject);
}
System.out.println("打印userDataJSONArray对象" + userDataJSONArray);
System.out.println("打印Map中的userTable对象" + map.get("userTable"));
}
@Test
public void recurringProblem() {
//首先从Map取出JSONArray
JSONArray jsonArray = map.get("userTable");
JSONArray userDataJSONArray= JSON.parseArray(jsonArray.toJSONString());
for (int i = 0; i < userDataJSONArray.size(); i++) {
JSONObject userJSONObject = userDataJSONArray.getJSONObject(i);
String content = userJSONObject.getString("content");
content += "+++模拟拼接content字段内容";
userJSONObject.put("content",content);
}
System.out.println("打印userDataJSONArray对象"+userDataJSONArray);
System.out.println("打印Map中的userTable对象"+map.get("userTable"));
}
学习永无止境,且行且珍惜。小宗于2021年5月22日19:39:53,济南。
共和国于今日痛失两位院士,愿世间如您所愿,医食无忧,一路走好!
我辈当自强!生命不息,学习不止!