java 深拷贝和浅拷贝
1.值传递和引用传递
值传递:是将变量的值复制了一份进行传递,当复制的变量值改变了不会影响原本的变量值。比如:int、double、String、Integer、Double等基本类型以及包装类都是值传递。
引用传递:引用传递一般是对于对象型变量而言的,传递的是内存中对象的地址,所以传递后的内容改变了原本的内容也会改变。比如:List、Map、对象等都是引用传递。
对于基本类型传递的是值,对于引用类型传递的是对象的内存地址的值,所以有时候你会看到有的教程说Java里只有一种方式就是值传递,这里要理解它的意思。
例如:changList可以不返回值就修改list,比如可以在changList中写对list的新增、删除、排序等,main方法中的list也会跟着被修改
public class Maintest { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list);// 输出 [1] changList(list); System.out.println(list);// 输出 [1, 2] } public static void changList(List<Integer> list) { list.add(2); } }
那万一我们想复制一份list数据传递到changList方法中,changList的list改变了而又不想改变main方法中的list呢,这就是我们下面要说的浅拷贝和深拷贝。
浅拷贝和深拷贝又被叫做浅复制和深复制,浅拷贝与深拷贝都是针对对象来说的。
浅拷贝:复制了一个对象,改变其中一个另外一个也会跟着改变,也就是上面说的引用传递。
深拷贝:复制了一个对象,两者完全隔离,也就是说改变其中一个另一个不受影响。
浅拷贝就像上面引用传递示例那样,本文后面主要讲的都是深拷贝。
如何实现深拷贝:
1.数组深拷贝
int[] a1 = {1, 2, 3, 4, 5}; int[] a2 = new int[a1.length]; System.arraycopy(a1, 0, a2, 0, a1.length);//System.arraycopy参数说明:(原数组, 原数组的开始位置, 目标数组, 目标数组的开始位置, 拷贝个数) System.out.println(Arrays.toString(a1)); // [1, 2, 3, 4, 5] System.out.println(Arrays.toString(a2)); // [1, 2, 3, 4, 5]
2. LIst<String>深拷贝
(1)java8以后可以用steam
List<String> oneList = new ArrayList<String>(); oneList.add("1"); oneList.add("2"); oneList.add("3"); List<String> twoList = oneList.stream().collect(Collectors.toList()); System.out.println(oneList);//[1, 2, 3] System.out.println(twoList);//[1, 2, 3]
(2)使用addAll
List<String> oneList = new ArrayList<String>(); oneList.add("1"); oneList.add("2"); oneList.add("3"); List<String> twoList = new ArrayList<String>(); twoList.addAll(oneList); System.out.println(oneList);//[1, 2, 3] System.out.println(twoList);//[1, 2, 3]
(3)使用Collections.copy
List<String> oneList = new ArrayList<>(); oneList.add("1"); oneList.add("2"); oneList.add("3"); List<String> twoList = new ArrayList<>(Arrays.asList(new String[oneList.size()]));//twoList要初始化大小 Collections.copy(twoList, oneList); System.out.println(oneList);//[1, 2, 3] System.out.println(twoList);//[1, 2, 3]
3.List<Object>深拷贝
例,Student类如下,实现了 Cloneable接口,并重写clone方法:
public class Student implements Cloneable { public Integer id; public String name; //省略get、set方法 @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } @Override public String toString() { return "id:" + id + ",name:" + name; } }
深拷贝实现:
public class Maintest { public static void main(String[] args) { Student student = new Student(); student.setId(1); student.setName("张三"); Student student1 = new Student(); student1.setId(2); student1.setName("李四"); List<Student> studentsList = new ArrayList<>(); studentsList.add(student); studentsList.add(student1); //克隆 List<Student> studentsList1 = studentsList.stream().map(v ->(Student) v.clone()).collect(Collectors.toList()); //测试,studentsList里张三的i的 studentsList.get(0).setId(99); System.out.println(studentsList); //[id:99,name:张三, id:2,name:李四] System.out.println(studentsList1); //[id:1,name:张三, id:2,name:李四] }
4.我推荐直接使用fastjson序列化深拷贝
Map<Long, Long> map = new HashMap<>(); map.put(1L,2L); map.put(3L,4L); String string = JSONObject.toJSONString(map); Map<Long, Long> map1 = JSON.parseObject(string,new TypeReference<Map<Long,Long>>(){}); List<Long> keysList = map1.entrySet().stream() .map(Map.Entry::getKey) .collect(Collectors.toList()); List<Long> valuesList = map1.entrySet().stream() .map(Map.Entry::getValue) .collect(Collectors.toList()); System.out.println("class " + keysList.get(0).getClass()); System.out.println("class " + valuesList.get(0).getClass());
5.我推荐使用SerializationUtils进行深拷贝,前提是当前类实现了Serializable接口,入HashMap
maven如下:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
实现如下:
import org.apache.commons.lang3.SerializationUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Maintest { public static void main(String[] args) { List<Integer> sco=new ArrayList<>(); sco.add(90); Address addre = new Address(); addre.setType("111"); addre.setValue("home"); List<Integer> sco1=new ArrayList<>(); sco1.add(80); Address addre1 = new Address(); addre1.setValue("home"); addre1.setType("222"); //student赋值 Student student = new Student(); student.setId(1); student.setName("张三"); student.setScore(sco); student.setAddress(addre); //student1赋值 Student student1 = new Student(); student1.setId(2); student1.setName("李四"); student1.setScore(sco1); student1.setAddress(addre1); List<Student> studentsList = new ArrayList<>(); studentsList.add(student); studentsList.add(student1); //使用SerializationUtils拷贝 List<Student> studentsList1 = (List<Student>)SerializationUtils.clone((Serializable)studentsList); //测试 studentsList.get(0).getScore().set(0,10); studentsList.get(0).getAddress().setValue("office"); //输出:[id:1,name:张三,score:[10],address:type:111,value:office, id:2,name:李四,score:[80],address:type:222,value:home] System.out.println(studentsList); //输出[id:1,name:张三,score:[90],address:type:111,value:home, id:2,name:李四,score:[80],address:type:222,value:home] System.out.println(studentsList1); } }