ArrayList源码解析(未完成)

ArrayList源码解析

简介

​ 特点:ArrayList是List接口的主要实现类,它存储有序的,可重复的数据。

​ 优缺点:ArrayList线程不安全,但其查询、修改数据的效率高。

​ 底层原理:ArrayList底层是基于数组来实现的。

Arraylist的继承与实现

ArrayList的类图(该图由Idea自动生成)

在这里插入图片描述
从上图可知,ArrayList继承了AbstractList,实现了Serializable、RandomAccess、Cloneable、List。

功能:

  • 继承AbstractList类,实现了List接口。它是一个抽象类,实现了 List 的一些位置相关操作(比如 get,set,add,remove),是第一个实现随机访问方法的集合类,但不支持添加和替换
  • 实现Cloneable接口,重写了函数clone(),可以被克隆。
  • 实现RandomAccess接口,提供了随机访问功能。
  • 实现了Serializable接口,因此,ArrayList支持序列化,可以将对象转换为可保持或可传输的过程。

源码解析

本文将从ArrayList使用的角度解析源码

ArrayList属性

在阅读源码前,我们需要先理解一些ArrayList属性代表的含义

	// 数组默认的初始容量为10
    private static final int DEFAULT_CAPACITY = 10;

	// 空的Object数组
    private static final Object[] EMPTY_ELEMENTDATA = {
    
    };

	// 空的Object数组,将其与上面的区分开来,EMPTY_ELEMENTDATA用于有参构造函数且参数为0时,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA用于无参构造函数。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
    
    };

	// 集合中存放数据的对象,ArrayList的核心
    transient Object[] elementData; 

	// 集合内数据的大小,size初始为0
    private int size;

	// modCount继承于AbstractList,用于记录数组的添加、删除操作次数

创建ArrayList对象

  1. 使用无参构造函数创建对象

    ArrayList arr = new ArrayList();
    

    在Idea中使用Ctrl+鼠标左键单击,可以进入ArrayList.java中查看源码。

    // 可以看出,创建了一个elementData的长度为0的ArrayList对象
    public ArrayList() {
          
          
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
  2. 使用有参构造函数创建对象,参数的类型为int。

    // 创建一个长度为initialCapacity的elementData数组
    public ArrayList(int initialCapacity) {
          
          
        // 如果初始容量长度大于0,则从新创建一个Object数组
        if (initialCapacity > 0) {
          
          
            this.elementData = new Object[initialCapacity];
        // 否则如果初始容量为0,则将EMPTY_ELEMENTDATA的地址赋给elementData。  
        } else if (initialCapacity == 0) {
          
          
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
          
          
        // 否则抛出异常    
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    
  3. 使用有参构造函数创建对象,参数的类型为Collection。

    // 使用这种方式创建ArrayList对象,会将Collection对象转为Object数组,然后将其拷贝给elementData数组
    public ArrayList(Collection<? extends E> c) {
          
          
        // 将Collection对象转为数组
        Object[] a = c.toArray();
        // 将b数组的长度赋值给size,判断其是否不等于0
        if ((size = a.length) != 0) {
          
          
            if (c.getClass() == ArrayList.class) {
          
          
                // 将a的地址赋给elementData
                elementData = a;
            } else {
          
          
                // 进行拷贝操作
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
          
          
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }
    

向arrayList集合中添加数据

1、add(E e)

add(E e)方法

功能:这个方法是将数据传入进来,按照数组自增的方式将其添加到数组中。

    public boolean add(E e) {
    
    
        // 判断集合内存储元素的个数加1看其是否可以承受这个容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal(int minCapacity)方法

功能:确保数组内部的容量大小可以添加新的数据

    private void ensureCapacityInternal(int minCapacity) {
    
    
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

calculateCapacity(Object[] elementData, int minCapacity)方法

功能:比较minCapacity(size+1)与DEFAULT_CAPACITY(10)的大小,返回最大值。

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    
    
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    
    
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

ensureExplicitCapacity(int minCapacity)方法

功能:minCapacity,判断其大小是否大于数组的长度,如果大于就需要对数组进行扩容操作

modCount++:数组的修改次数+1,之后会讲到这个属性的功能。

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow(int minCapacity)方法

功能:对数组进行扩容

    private void grow(int minCapacity) {
    
    
        // 将数组的长度定义为集合中旧的容量
        int oldCapacity = elementData.length;
        // 将新的容量定义为旧的容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新的容量小于旧的容量,就令新的容量等于旧的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果新的容量减去最大数组长度(Integer.MAX_VALUE - 8),则令新的容量扩充为int类型的最大取值范围
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 将旧的数组的数据复制到新的数组中去
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

此时结束前面对数组的扩容判断之后,将新的数据添加到数组上。

elementData[size++] = e;
        return true;

重点:集合在初始化时,它是一个空数组,只有当第一次添加数据时,它的长度才会变为10

2、add(int index, E element)

add(int index, E element)方法

功能:该方法的功能是向按照指定的位置,向集合中插入元素

    public void add(int index, E element) {
    
    
        // 用于add和addAll的范围检查,判断是否越界,否则抛出异常。
        rangeCheckForAdd(index);

        // 确保数组内部容量可以添加新的数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将elementData中从index开始的数据拷贝到elementData从index+1开始的位置。
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 将element元素添加到指定的index位置上
        elementData[index] = element;
        // 集合大小+1
        size++;
    }

3、addAll(Collection<? extends E> c)

功能:将collection集合中的数据添加到arrayList集合中

    public boolean addAll(Collection<? extends E> c) {
    
    
        // 将集合转为数组
        Object[] a = c.toArray();
        int numNew = a.length;
        // 确保数组的容量可以添加这些数据,如果不行就扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // 将a数组内的元素添加到elementData[size]之后.
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

移除arrayList集合中的数据

1、remove(int index)

功能:移除指定位置上的元素

    public E remove(int index) {
    
    
        // 查看index是否不在集合范围中
        rangeCheck(index);

        // 修改次数+1
        modCount++;
        // 返回指定删除位置上的元素
        E oldValue = elementData(index);

        // 将指定位置后的元素集体向前移动一位
        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;
    }

2、remove(Object o)

功能:移除指定元素

// 通过for循环遍历数组,获取需要删除元素的下标,通过remove删除。
public boolean remove(Object o) {
    
    
        if (o == null) {
    
    
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
    
    
                    fastRemove(index);
                    return true;
                }
        } else {
    
    
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
    
    
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

3、removeRange(int fromIndex, int toIndex)

功能:移除fromIndex到toIndex范围内的元素

    protected void removeRange(int fromIndex, int toIndex) {
    
    
        // 修改次数+1
        modCount++;
        // 需要向前移动的的长度
        int numMoved = size - toIndex;
        
        // 将toIndex后的元素向前移动numMoved
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        // 通过for循环置从newSize开始的元素为空
        for (int i = newSize; i < size; i++) {
    
    
            elementData[i] = null;
        }
        // 更新size的大小
        size = newSize;
    }

返回指定位置上的元素

get(index)

// 先进行范围检查,然后直接返回数组中下标为index的元素    
public E get(int index) {
    
    
    // index不能>=当前集合的大小
    rangeCheck(index);

    return elementData(index);
}

更新指定位置上的元素

set(int index, E element)

// 先进行范围检查,然后获取指定位置上的元素,然后存放到oldValue中,将需要更新的元素赋值给elementData[index],最后返回旧的数据。
public E set(int index, E element) {
    
    
    // index不能>=当前集合的大小
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

判断集合中是否包含某一个元素

contains(Object o)

功能:判断集合中是否包含某一个元素

public boolean contains(Object o) {
    
    
	// 判断下标是否大于等于0(表示元素存在)
    return indexOf(o) >= 0;
}

indexOf(Object o)

功能:获取集合中存在的元素的下标

public int indexOf(Object o) {
    
    
    if (o == null) {
    
    
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
    
    
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    // 返回-1表示元素不存在
    return -1;
}

清空集合中全部的数据

public void clear() {
    
    
    // 修改次数+1
    modCount++;

    // 使用for循环令数组中全部的元素都为空
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

注意:在删除或清空集合中的元素时,集合中的数据大小(size)发生改变,而数组的长度(length)不会发生改变。

获取集合内指定范围内的元素

subList(int fromIndex, int toIndex)方法

功能:获取集合内指定范围内的元素

public List<E> subList(int fromIndex, int toIndex) {
    
    
    // 先进性范围检查,查看fromIndex、toIndex是否满足1条件
    subListRangeCheck(fromIndex, toIndex, size);
    
    // 返回一个新创建的SubList对象,将需要获取的范围传入其中。
    return new SubList(this, 0, fromIndex, toIndex);
}

SubList类

介绍:它是ArrayList类里面的一个内部类,它的作用和ArrayList类的作用一样,它可以获取数组中指定位置的元素,而不是截取他们成为一个新的数组。

在SubList类中同样定义了修改、删除、添加、查询功能。

迭代器

iterator()方法

返回一个新创建的Itr对象

public Iterator<E> iterator() {
    
    
    return new Itr();
}

Itr类

介绍:这个类和SubList类一样,也是一个内部类。Itr实现了Iterator接口。所以我们可以使用它进行遍历。

fail-fast机制

介绍:在ArrayList的使用中,可能会抛出一种异常,这种异常多数会出现在使用多线程的时候。

这个异常的名称为ConcurrentModificationException。

产生原因:在多个线程同时对集合进行操作时,某个线程对集合进行迭代,会将这个时间段的modCount赋值给Itr类中的expectedModCount属性,而其他线程在同一时间段对集合进行操作时modCount会发生更新,而expectedModCount不会进行更新,在迭代器中会有一个方法(checkForComodification())对modCount与expectedModCount的值进行比对,所以当二者的值不相等时,就会抛出异常。这就是fail-fast机制。

同时这也是为什么ArrayList集合不安全的原因。

fail-fast目的:为了防止读数据时产生线程安全问题而提出的机制,它是一个线程安全机制。

详情可以参考这位博主的帖子。

参考连接:

https://blog.csdn.net/zymx14/article/details/78394464

为了解决线程安全问题,我们可以使用CopyOnWriteArrayList代替ArrayList。

猜你喜欢

转载自blog.csdn.net/weixin_43898463/article/details/114668460