Map of Java in the deep copy and shallow copy

Background: The project needs to be maintained map to copy some operations, but do not want to affect the map of the original, beginning directly = assignment to the new map, but found when debugging original map has changed, only I realize that they made a mistake copying, collating information below to facilitate follow-up review.

The reference copy of an object to another object, a total of three ways. The first way is to direct assignment, the second embodiment is shallow copy, the third copy is deep. These three concepts were to copy objects.

java assignment is to copy the object reference, if we want to get a copy of an object, use the assignment operator is unable to achieve the purpose of:

@Test
public void testassign(){
  Person p1=new Person();
  p1.setAge(31);
  p1.setName("Peter");
 
  Person p2=p1;
  System.out.println(p1==p2);//true
}

If you create a new copy of the object, that is exactly the same as their initial state, but can later change their status, and independently of each other, you need to use to copy the java objects, such as native clone () method.

  • How to clone objects

Object object has a clone () method, to achieve the objects of each attribute in the copy, but it is protected in the visible range, the entity class cloning the premise that:

  • ① implement the Cloneable interface, which is a marker interface itself is not a method.

  • ② covering the clone () method, to enhance the visibility for the public.

@Data
public class Person implements Cloneable {
    private String name;
    private Integer age;
    private Address address;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
 
@Test
public void testShallowCopy() throws Exception{
  Person p1=new Person();
  p1.setAge(31);
  p1.setName("Peter");
 
  Person p2=(Person) p1.clone();
  System.out.println(p1==p2);//false
  p2.setName("Jacky");
  System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
  System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

Members of the test only two basic types of testing to achieve the purpose.

It looks like things are not so simple, as a member of a Person increase the Address class:

@Data
public class Address {
    private String type;
    private String value;
}

Test again, the problem came.

@Test
public void testShallowCopy() throws Exception{
  Address address=new Address();
  address.setType("Home");
  address.setValue("北京");
 
  Person p1=new Person();
  p1.setAge(31);
  p1.setName("Peter");
  p1.setAddress(address);
 
  Person p2=(Person) p1.clone();
  System.out.println(p1==p2);//false
 
  p2.getAddress().setType("Office");
  System.out.println("p1="+p1);
  System.out.println("p2="+p2);
}

Check out:

false
p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

'Ve got a problem, only modify the address type p2 of the two address types have become Office.

  • Deep and shallow copy copy

Previous examples shallow vs. deep copy typical use case.

Shallow copy: all values ​​are copied attribute contains the same object with the original object, all object references and attributes still point to the original object.

Deep copy: Based on the shallow copy, all references to variables other objects have also been clone, and points to the new object is copied before.

In other words, a default clone () method implementation mechanism is still assignment.

If a copy of the property are the basic types, then you only need to achieve cloneable mechanism of the current class on it, this is a shallow copy.

If the attribute is copied object contains another entity object reference, then these objects need to implement the entity classes and interfaces cloneable cover clone () method.

@Data
public class Address implements Cloneable {
    private String type;
    private String value;
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Re-run the test:

false
p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
  • deep copy clone embodiment Summary

① If there is a non-native members, such as members of the self-defined objects, you will need:

The members implement the Cloneable interface and cover the clone () method, do not forget to enhance the visibility for the public. Meanwhile, modifications clone is copied class () method of increasing the cloning of the logic member.

② If the copied object is not directly inherited Object, there are other intermediate inheritance hierarchy, each layer super class needs to implement Cloneable interface and cover the clone () method.

And members of different objects, inheritance clone in class does not need to be replicated clone () to do the extra work.

Words, if the full realization of deep copy, need to be replicated object inheritance chain, a reference to each object on the chain implements clone mechanism.

Examples of the foregoing may also be acceptable, if there are N object members, inheritance relationship M layer will be very troublesome.

  • Achieved using a deep copy serialization

clone strongly typed mechanism is not limited, and such is not mandatory to achieve the Cloneable object inheritance chain is achieved; not mandatory cover clone () method. Therefore, the encoding process relatively easy to overlook one of the links for troubleshooting complex projects is difficult.

To find a reliable, simple way, it is a way to serialize.

Are copied object inheritance chain, each object reference chains implement java.io.Serializable. This is relatively simple, do not need to implement any method, serialVersionID requirement is not mandatory, it is not wrong for a deep copy.

Realize their deepClone method, this flow is written, read out. Commonly known as: freeze - thaw.

  • Back to the map issue before

The method may be used to achieve deep copy putAll shallow copy.

Map object can only be achieved using a shallow copy:

public class CopyMap {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put( "key1", 1);
        Map<String,Integer> mapFirst = new HashMap<String,Integer>();
        mapFirst.putAll(map); 
        System. out.println(mapFirst);
        map.put( "key2", 2);
        System. out.println(mapFirst);
    }
}

Map of how to implement a deep copy of it?

One method is to use a sequence of ways to achieve the object of deep copy, but the premise is, the object must be before they can implement Serializable interface, Map itself does not implement the Serializable interface, so in this way can not be serialized Map, that is, not deep copy Map. But HashMap is possible, since it implements Serializable. In the following manner, HashMap terms based on non Map of copies.

Specific achieve the following:

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){         
        T clonedObj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close(); 
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            clonedObj = (T) ois.readObject();
            ois.close();  
            }catch (Exception e){
                e.printStackTrace();
            }
        return clonedObj;
     }
}

How to use it, the following is an example of the use of, and proves putAll Map of methods did not achieve a deep copy, putAll only basic data types play a role in a deep copy.

For example:

public static void main(String[] args) {    
    List<Integer> list = new ArrayList<Integer>();
    list.add(100);
    list.add(200);           
    HashMap<String,Object> map = new HashMap<String,Object>();
    //放基本类型数据
    map.put("basic", 100);
    //放对象
    map.put("list", list); 
    HashMap<String,Object> mapNew = new HashMap<String,Object>();
    mapNew.putAll(map);
             
    System.out.println("----数据展示-----");
    System.out.println(map);
    System.out.println(mapNew);          
    System.out.println("----更改基本类型数据-----");
    map.put("basic", 200);
    System.out.println(map);
    System.out.println(mapNew);
         
    System.out.println("----更改引用类型数据-----");
    list.add(300);
    System.out.println(map);
    System.out.println(mapNew);
             
    System.out.println("----使用序列化进行深拷贝-----");
    mapNew = CloneUtils.clone(map);
    list.add(400);
    System.out.println(map);
    System.out.println(mapNew);
}

Output:

----数据展示----
{basic=100, list=[100, 200]}
{basic=100, list=[100, 200]}
----更改基本类型数据----
{basic=200, list=[100, 200]}
{basic=100, list=[100, 200]}
----更改引用类型数据----
{basic=200, list=[100, 200, 300]}
{basic=100, list=[100, 200, 300]}
----使用序列化进行深拷贝----
{basic=200, list=[100, 200, 300, 400]}
{basic=200, list=[100, 200, 300]}

The top two raw data, using a method of copying a putAll mapNew new objects,

Middle of the two, is a modification of the basic data types map object, and did not affect the mapNew object.

But look at the second group of the countdown, change the reference data types, they found value mapNew also changed, so putAll did not have a deep copy of the map.

The final surface is in serialized fashion, was found to change the type of data referenced when, mapNew object is not changed, there arises a deep copy.

The above-described tools, deep copy object can be achieved, the HashMap is not limited, provided that implements Serlizeable interface.

  • Refer to the original


1、 https://blog.csdn.net/u012611620/article/details/81698596

2、 https://www.cnblogs.com/cxxjohnson/p/6258742.html

Guess you like

Origin www.cnblogs.com/jlutiger/p/11201071.html