Object reference of ArrayList in Java

Time:2019-12-10

Preface

The cause of the incident was due to the use of colleaguesArrayListBased on the structural method of belt parameterArrayListObject copying, modifying NEWArrayListThe member variable of the element (object) in the object will also be modifiedArrayListThe member variable of the element (object) in.

The following will reproduce the problems encountered to you through the duplicate code

Duplicate code

User class

public class User {

    private Integer id;

    private String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Example of problem recurrence

import java.util.ArrayList;
import java.util.List;

public class ArrayListReference {

    public static void main(String[] args) {
        //Original user list
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            users.add(new User(i, "test"));
        }
        //New user list
        List<User> newUsers = new ArrayList<>(users);
        for (int j = 0; j < newUsers.size(); j++) {
            //Modify the user name of the new user list
            newUsers.get(j).setName(String.valueOf(j));
        }
        //Print new user list
        System.out.println("newUsers:" + newUsers);
        //Reprint original user list
        System.out.println("After update newUsers,users:" + users);
    }
}

Sample run results

users:[User{id=0, name='test'}, User{id=1, name='test'}, User{id=2, name='test'}, User{id=3, name='test'}, User{id=4, name='test'}, User{id=5, name='test'}, User{id=6, name='test'}, User{id=7, name='test'}, User{id=8, name='test'}, User{id=9, name='test'}]
newUsers:[User{id=0, name='0'}, User{id=1, name='1'}, User{id=2, name='2'}, User{id=3, name='3'}, User{id=4, name='4'}, User{id=5, name='5'}, User{id=6, name='6'}, User{id=7, name='7'}, User{id=8, name='8'}, User{id=9, name='9'}]
After update newUsers,users:[User{id=0, name='0'}, User{id=1, name='1'}, User{id=2, name='2'}, User{id=3, name='3'}, User{id=4, name='4'}, User{id=5, name='5'}, User{id=6, name='6'}, User{id=7, name='7'}, User{id=8, name='8'}, User{id=9, name='9'}]

Analysis

problem

Why is it usedArrayListTo reconstruct a newArrayListAfter, operate newArrayListThe element in the object will affect the originalArrayListWhat about the elements in?

First of all, we need to analyzeArrayListConstruction method of

ArrayList source code analysis

The following is what is called in the example.ArrayListSource code of construction method

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                //Here is the key code. Here is the copy method of array elements
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

From the source code, we know that the key code of array replication is

elementData = Arrays.copyOf(elementData, size, Object[].class);

Next entryArrays.copyOf()Source code for research

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        //Construct a new array object
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        //Copy the original array elements to the new array
        System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
        return copy;
    }

From the above source code, we know that the key code is

System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

Below isSystem.arraycopy()Source code of method

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

BecauseSystem.arraycopy()Method isnativeMethod, it is difficult to trace its implementation code. However, you can know the characteristics of this method from the method annotation:

Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array. A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest. The number of components copied is equal to the length argument. The components at positions srcPos through srcPos+length-1 in the source array are copied into positions destPos through destPos+length-1, respectively, of the destination array.

The translation result is

Copies the array from the specified source array (starting at the specified location) to the specified location of the target array. Copy the subsequence of the array component from the source array referenced by SRC to the target array referenced by dest. The number of copied components is equal to the length parameter. The components in the source array at srcpos + length-1 are copied to the destpos in the target array at destpos + length-1.

SinceArrayListThe construction method of is to copy the new array, so why? Here is the conclusion in advance:When an array element is an object, it actually stores a reference to the object,ArrayListCopying arrays only copies references to objects. That’s why the first question comes up

Re verification

The following will use a copy example of an array to verify the conclusion, using the==To compare whether object references are the same

Example of problem recurrence

import java.util.Arrays;

public class ArrayReference {

    public static void main(String[] args) {
        //Original user list
        User[] users = new User[10];
        for (int i = 0; i < users.length; i++) {
            users[i] = (new User(i, "test"));
        }
        //New user list
        User[] newUsers = Arrays.copyOf(users, users.length);
        for (int j = 0; j < users.length; j++) {
            //Compare object references
            System.out.println(j + ":" + (users[j] == newUsers[j]));
        }
    }
}

Sample run results

0:true
1:true
2:true
3:true
4:true
5:true
6:true
7:true
8:true
9:true

Result analysis

It can be seen from the operation results that the above conclusions are correct. NamelyWhen an array element is an object, it actually stores a reference to the object

Terms of settlement

The solution is very simple. You only need to traverse the elements in the object array, call the object construction method to construct a new object and add it to the new array

Solution example

public class ArrayListReferenceSolution {

    public static void main(String[] args) {
        //Original user list
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            users.add(new User(i, "test"));
        }
        //New user list
        List<User> newUsers = new ArrayList<>();
        for (int j = 0; j < users.size(); j++) {
            //Using construction methods to construct new objects
            newUsers.add(new User(users.get(j).getId(),users.get(j).getName()));
        }
        for (int k= 0; k < users.size(); k++) {
            //Compare object references
            System.out.println(k + ":" + (users.get(k) == newUsers.get(k)));
        }
    }
}

Sample run results

0:false
1:false
2:false
3:false
4:false
5:false
6:false
7:false
8:false
9:false

Result analysis

From the running results, we can see that the method in the example can be used to duplicate one without disturbing the originalArrayListThe object.