ArrayList源码解析

首先我们对Jdk中集合类的命名做一个简单的梳理。ArrayList即Array+List表示这是一个集合,它的数据结构使用的数组。同理LinkedList表示这是其数据结构是链表;HashMap表示其基于hash算法实现的(本质是数组+链表+红黑树);TreeMap其数据结构是红黑树;LinkedHashMap底层是HashMap,同时进一步做了封装,对外展示是链表结构。说了这么多,我想说的是Jdk中的命名基本都是见名知意的,这些都是值得我么学习。好了,不扯了,进入正题。

1.ArrayList特征:

List有索引、有序、元素可重复。查询快,新增删除慢。

为啥这么说,通过了解它的存储结构就会明白。

2.实现接口:

ArrayList源码解析_第1张图片

List、RandomAccess、Cloneable、Serializable

实现RandomAccess便于集合元素的快速访问即查询。

实现Cloneable便于集合的拷贝,本质是数组的复制,属于浅拷贝。

实现Serializable便于集合的序列化

3.成员变量:

初始容量10、最大容量Integer.MAX_VALUE – 8(和jvm相关),元素存储位置Object[]、集合大小size。

数据结构:Object[]

//数组初始化大小
private static final int DEFAULT_CAPACITY = 10;

//数组最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//存储元素的对象数组
transient Object[] elementData;

//数组中元素的个数
private int size;

4.构造方法::

可指定初始容量、或者直接传递一个集合实现类来构造ArrayList

public ArrayList(int initialCapacity) {}

public ArrayList() {}

public ArrayList(Collection c) {}

5.元素查询::

    public E get(int index) {
        //检查索引是否越界
        rangeCheck(index);

        //从数组中取值
        return elementData(index);
    }

1.通过索引获取元素时,首先检查索引是否越界。

2.从数组中取值。查询非常简单,我们知道数组在堆内存开辟的是一块连续的空间,通过索引能够很快计算出元素的存储位置,这也是为什么查询快的原因。

6.元素新增::

    public boolean add(E e) {
        //插入前的操作,主要是看数组是否初始化,是否需要扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //数值复制,数值元素个数size增加
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        //数组初始化,默认长度是10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        //size+1大于数组长度时扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        // overflow-conscious code
        //数组长度变为原来的1.5倍
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //数组的复制,将元素从旧数组中搬运到新数组中。非常耗性能
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

1.添加元素之前,先做数组的初始化工作,数组长度默认为10。

2.当size+1大于数组长度时,进行扩容

3.扩容时,新数组的长度变为原来的1.5倍。并进行数组元素的复制,这个过程非常耗性能,所以建议构造数组时指定容量。

4.元素存储到数组指定索引处。

7.元素删除:

    public E remove(int index) {
        //索引越界减产
        rangeCheck(index);

        //数组结构变更次数增加1,对于新增、删除操作会影响数组的结构
        modCount++;
        E oldValue = elementData(index);

        //数组元素位置调整,删除元素后,index后的所有元素往前移一个位置
        //numMoved 需要移动的元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

8.元素修改:

    public E set(int index, E element) {
        //检查索引是否越界
        rangeCheck(index);

        //元素替换,新值替换旧值
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

9.线程安全问题:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //位置1,多线程存在风险
        elementData[size++] = e;
        return true;
    }

ArrayList是线程不安全的。当多线程访问公共变量arrayList进行add(e)方法时,当线程1在位置1出完成了复制操作,但是size还未增加1,此时线程2进来添加元素时会覆盖掉线程1的值。即 elementData[size++] = e并非一个原子操作,会有多线程安全问题。

10.深拷贝、浅拷贝的概念:

Java中对象的复制通常通过赋值来实现,但是这种方式通过任何一方的引用操作都会改变对象。

浅拷贝:对象实现Cloneable接口,重写clone()方法,可以实现对象的复制,得到一个新的对象。对于一个对象中的属性既包含基本类型又包含引用类型,clone方法完成的拷贝是浅拷贝,因为引用类型的对象拷贝的是引用。

深拷贝:就是让对象中的任何一个属性,包括基本数据类型和引用数据类型都是不相同的,这个拷贝得到的对象就是一个深拷贝的对象。这种方法需要各个引用类型都实现Cloneable接口,重写clone方法。最后在拷贝对象clone方法中,逐一调用其属性对象的clone()方法。

以List为例,直接调用ArrayList的clone方法属于浅拷贝,即ArrayList对象是新对象,但是里面的Student元素是相同的,新老集合指向相同的Student引用。

代码示例:其中Student对象是Person对象中的一个属性。

public class AnnotationController{
    public static void main(String[] args) throws CloneNotSupportedException {
        //浅拷贝 拷贝对象实现Cloneable接口,拷贝对象的成员变量都是基础数据类型
        shallowCloneWithBaseDataType();

        //浅拷贝 拷贝对象的成员变量有基础数据类型和引用数据类型,引用数据类型拷贝的是引用
        shallowCloneWithMultiDataType();

        //深拷贝 拷贝对象的成员变量有基础数据类型和引用数据类型,每个对象都需要重写clone方法
        //并且通过顶层对象,调用每一个对象的clone方法。
        deepClone();
    }

    public static void shallowCloneWithBaseDataType() throws CloneNotSupportedException {
        Student orgStudent = new Student(10,"小华");
        Student copyStudent = (Student) orgStudent.clone();
        copyStudent.setName("小明");
        //copyStudent是新对象,输出为false
        System.out.println(orgStudent.getName().equals(copyStudent.getName()));
    }

    public static void shallowCloneWithMultiDataType() throws CloneNotSupportedException {
        Student orgStudent = new Student(10,"小华");
        Person orgPerson = new Person(1, orgStudent);
        Person clonePerson = (Person)orgPerson.clone();
        clonePerson.setType(2);
        orgStudent.setName("小美");
        System.out.println(orgPerson.getType() == clonePerson.getType());
        System.out.println(orgPerson.getStudent().getName().equals(clonePerson.getStudent().getName()));
    }

    public static void deepClone() throws CloneNotSupportedException {
        Student orgStudent = new Student(10,"小华");
        Person orgPerson = new Person(1, orgStudent);
        Person clonePerson = (Person)orgPerson.clone();
        clonePerson.setType(2);
        orgStudent.setName("小美");
        System.out.println(orgPerson.getType() == clonePerson.getType());
        System.out.println(orgPerson.getStudent().getName().equals(clonePerson.getStudent().getName()));
    }


}

@Data
@AllArgsConstructor
class Person implements Cloneable{
    int type;
    Student student;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //return super.clone();

        //深拷贝,需要调用属性对象的clone方法
        Person person = (Person) super.clone();
        person.setStudent((Student) person.getStudent().clone());
        return person;
    }
}

@Data
@AllArgsConstructor
class Student implements Cloneable{
    int age;
    String name;

    //若是对Person的浅拷贝,可不实现Cloneable。
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 

你可能感兴趣的:(Java基础)