今天在检查自己写的代码(bug)时,通过调试,发现了bug产生的原因原来是集合拷贝问题。修改拷贝后的集合属性时,源集合的属性也被修改了,我是通过new ArrayList(原集合)这种方式来拷贝的。于是我尝试对代码进行修改,历经多次调整,最后我改成了这样,成功了(苦笑):
List<InsightOutputDiffSumMain> list = new ArrayList<>();
for (InsightOutputDiffSumMain in:incomeList){
InsightOutputDiffSumMain sumMain = new InsightOutputDiffSumMain(); //new一个对象
sumMain.setZero(new InsightOutputDiffSumTaxRateDetail(in.getZero().getPrice(),in.getZero().getDiff()));
sumMain.setZeroPointFive(new InsightOutputDiffSumTaxRateDetail(in.getOnePointFive().getPrice(),in.getOnePointFive().getDiff()));
sumMain.setOne(new InsightOutputDiffSumTaxRateDetail(in.getOne().getPrice(),in.getOne().getDiff()));
sumMain.setOnePointFive(new InsightOutputDiffSumTaxRateDetail(in.getOnePointFive().getPrice(),in.getOnePointFive().getDiff()));
sumMain.setTwo(new InsightOutputDiffSumTaxRateDetail(in.getTwo().getPrice(),in.getTwo().getDiff()));
sumMain.setThree(new InsightOutputDiffSumTaxRateDetail(in.getThree().getPrice(),in.getThree().getDiff()));
sumMain.setFour(new InsightOutputDiffSumTaxRateDetail(in.getFour().getPrice(),in.getFour().getDiff()));
sumMain.setFive(new InsightOutputDiffSumTaxRateDetail(in.getFive().getPrice(),in.getFive().getDiff()));
sumMain.setSix(new InsightOutputDiffSumTaxRateDetail(in.getSix().getPrice(),in.getSix().getDiff()));
sumMain.setNine(new InsightOutputDiffSumTaxRateDetail(in.getNine().getPrice(),in.getNine().getDiff()));
sumMain.setTen(new InsightOutputDiffSumTaxRateDetail(in.getTen().getPrice(),in.getTen().getDiff()));
sumMain.setEleven(new InsightOutputDiffSumTaxRateDetail(in.getEleven().getPrice(),in.getEleven().getDiff()));
sumMain.setThirteen(new InsightOutputDiffSumTaxRateDetail(in.getThirteen().getPrice(),in.getThirteen().getDiff()));
sumMain.setSixteen(new InsightOutputDiffSumTaxRateDetail(in.getSixteen().getPrice(),in.getSixteen().getDiff()));
sumMain.setSeventeen(new InsightOutputDiffSumTaxRateDetail(in.getSeventeen().getPrice(),in.getSeventeen().getDiff()));
sumMain.setProjectName(in.getProjectName());
list.add(sumMain);
}
因为集合元素的属性是对象,所以我需要拷贝对象的所有属性,还好属性只有2个。 看到这段代码我不禁怀疑自己,所以我开始寻求更简洁的解决方法,于是我就去了解学习了集合拷贝,分享给大家。
List集合的几种拷贝方式
- new ArrayList()
- Object.clone()
- BeanUtils.copyProperties()
- Collections.copy()
- list.addAll()
下面我们依次展开讨论
准备工作
- 创建2个实体类:Student(学生)、Class(班级)
Student:
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class Student {
private String name; //姓名
private Class theClass; //班级
}
Class:
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class Class {
private String className; //班级名称
private Integer classMembers; //班级人数
}
- 便于测试,封装打印公用方法
//打印方法
static void print(List<Student> source,List<Student> copy){
//原集合
for (Student s: source){
System.out.println(s);
}
System.out.println("-----------------------------华丽的分割线-------------------------------");
//复制集合
for (Student s: copy){
System.out.println(s);
}
}
- 准备数据
public static void main(String[] args) {
//创建2个班级
Class class1 = new Class("1班", 40);
Class class2 = new Class("2班", 30);
//创建4个学生
Student student1 = new Student("学生1",class1);
Student student2 = new Student("学生2",class1);
Student student3 = new Student("学生3",class2);
Student student4 = new Student("学生4",class2);
//学生加入集合中
List<Student> students = new ArrayList<>();
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
}
第一种方式:new ArrayList()
//复制集合
List<Student> copyStudents = new ArrayList<>(students);
查看修改前原集合和复制集合:
遍历输出查看原集合和复制集合:
//对复制的集合进行修改,修改班级为2班
for (Student s: copyStudents){
s.setTheClass(class2);
}
查看结果:
可以看出对复制集合修改的时候原集合也被修改了,这其实是浅拷贝,关于深拷贝和浅拷贝在讨论完集合的集中拷贝方法之后我们再讨论。
第二种方式:Object.clone()
使用list的clone()方法,这里注意只能使用ArrayList而不能使用List,因为在Object中clone()方法使用protected方法修饰,ArrayList重写的clone()方法并且是public修饰
//学生加入集合中
ArrayList<Student> students = new ArrayList<>();
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
//新建集合
ArrayList<Student> copyStudents = (ArrayList<Student>) students.clone();
打印原集合和复制集合
第三种方式:BeanUtils.copyProperties()
使用org.springframework.beans包下的BeanUtils拷贝
遍历students集合,取出其中的元素然后调用 BeanUtils.copyProperties();
for (Student s: students){
Student newStudent = new Student();
BeanUtils.copyProperties(s, newStudent);
copyStudents.add(newStudent);
}
上面这段代码需要BeanUtils相关依赖,于是我懒得添加相关依赖,直接手动get、set
//复制
for (Student s: students){
Student newStudent = new Student();
newStudent.setName(s.getName());
newStudent.setTheClass(s.getTheClass());
copyStudents.add(newStudent);
}
//修改复制数组
for (Student s: copyStudents){
s.setTheClass(class2);
}
可以看到修改复制集合的theClass并没有修改原集合的值。但是,注意我这里是修改的Student的属性theClass对象,当我修改的是student的属性theClass的属性时,原集合的也会被修改。
for (Student s: copyStudents){
s.getTheClass().setClassName("3");
}
查看结果:
看到这里,相信大家知道我最上面的代码为啥要new两次对象了吧。
第四种方式:Collections.copy()
Collections.copy(target,source)有几个注意事项
1.目标集合在前,源集合在后
2.目标集合元素大小必须大于或等于源集合元素个数,否则会异常
在使用这个方法时,可以先将目标集合加入和原集合元素个数相同数量的空值
//目标集合
ArrayList<Student> copyStudents = new ArrayList<>(Arrays.asList(new Student[students.size()]));
然后调用打印方法,查看结果:
Collections.copy(copyStudents,students);
修改复制的集合
for (Student s: copyStudents){
s.getTheClass().setClassName("3");
}
结果也是修改复制集合影响原集合
第五种方式:list.addAll()
addAll,顾名思义就是将所有元素加入到集合中,最后调用了System.arraycopy()方法
测试修改:结果也是一样引用传递
上面介绍的只是部分方法,还有很多实现方式这里就不讨论了(我也不会)
最后,我使用了fastjson提供的JSONArry的方法进行了集合复制
List<InsightOutputDiffSumMain> list = new ArrayList<>();
//拷贝集合
JSONArray jsonArray = (JSONArray) JSONArray.toJSON(incomeList);
List sumMains = JSONArray.toJavaObject(jsonArray, List.class);
for (Object main : sumMains) {
list.add(JSONArray.toJavaObject((JSONObject) main, InsightOutputDiffSumMain.class));
}
在我写这里的时候我又学到了另一种类似的方式,感觉更清晰
List<InsightOutputDiffSumMain> list = new ArrayList<>();
JSONArray jsonArray = new JSONArray();
jsonArray.addAll(incomeList);
String jsonString = jsonArray.toJSONString();
list = JSON.parseArray(jsonString,InsightOutputDiffSumMain.class);
我的理解(当然还有参考资料),浅拷贝是拷贝对象的引用地址,复制的对象和原对象共享同一堆内存。所以修改其中一个对象另一个也会跟着改变。深拷贝是复制对象,开辟新的空间,创建和原对象一摸一样的对象,不和原对象共享内存,修改复制对象不会对原对象产生影响。
写在最后: 在以后的学习工作中,希望自己能够坚持写文章,和大家探讨,如有错误之处,请大家指出,希望能和大家一起成长。