秋招之路1:ArrayList的底层实现原理

Posted whyaza

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秋招之路1:ArrayList的底层实现原理相关的知识,希望对你有一定的参考价值。

ArrayList

概述

  1. ArrayList 是基于数组实现的,是一个动态数组
  2. ArrayList 不是线程安全的,只能在单线程环境下;多线程使用ArrayList,应该考虑Collections.synchronizedList(List l)和concurrent并发包下的CopyOnWriteArrayList类
  3. ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输;
    实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问;
    实现了Cloneable接口,能被克隆。

首先,我的length和size有点混

1.length属性是针对Java中的数组来说的,要求数组的长度可以用其length属性;
2.length()方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
3.java中的size()方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!

elementData

transient Object[] elementData:
transient:用transient关键字标记的成员变量不参与序列化过程。

size

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial        这个是什么意思呢?说明一个序列化属性
     */
    private int size;

构造方法

一共三种:
//指定初始大小的构造方法
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

//默认:
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }     

//使用集合
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();    //转换为数组,这个数组中的类型是根据c判断的,有可能不是Object[]
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)    //不是Oject[]时候,利用copyOf进行转换
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

常常出现的modCount,expectedModCount变量

这一变量常常和线程安全有关,是记录修改次数的两个变量

add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
//调用下面的这个方法
private void ensureCapacityInternal(int minCapacity) {
        //用来判定是否是初次增加。
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {    
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
//调用下面这个方法
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
//看看本次增加的值,是否大于elementData了,大于再增加
            grow(minCapacity);
    }
//调用下面的这个方法
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
//每次成1.5倍的增长
        if (newCapacity - minCapacity < 0)
//1.5倍都不够,就直接用minCapacity作为新容量
            newCapacity = minCapacity;
//是否超过MAX_ARRAY_SIZE[一般虚拟机的防溢出的最大内存]
        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);
    }

add方法的另外一个重载

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

//作用就是: 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。

主要涉及这个方法
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
代码解释:
  Object src : 原数组
int srcPos : 从元数据的起始位置开始
  Object dest : 目标数组
  int destPos : 目标数组的开始起始位置
  int length : 要copy的数组的长度

add的另一个重载

//从指定的位置开始,将指定collection中的所有元素插入到此列表中。  
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

get方法

public E get(int index) {
        rangeCheck(index);    
//检查是否越界
        return elementData(index);    
//抑制返回值警告的一个方法
    }

set方法

// 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。  
public E set(int index, E element) {
        rangeCheck(index);

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

remove

有三种实现

//移除制定位置上的元素
public E remove(int index) {
        rangeCheck(index);

        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;
    }

//移除指定元素:
remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。
fastRemove:基本是和remove(int index)一致,但是跳过了边界检查条件。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) { //java 判断null的方法。
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { //java的.equals是判断是否是值相等
fastRemove(index);
return true;
}
}
return false;
}
//removeRange
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);

    // clear to let GC do its work    //后面的null元素,让gc去回收。
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

toArray方法

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
//这个toArray方法,主要是用于array转换成特定的比如是String[]等数组时候用的
//链接门[https://www.cnblogs.com/goloving/p/7693388.html]
public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

Fail-Fast机制:

ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。
具体参见HashMap的实现原理 中的Fail-Fast机制。

以上是关于秋招之路1:ArrayList的底层实现原理的主要内容,如果未能解决你的问题,请参考以下文章

ArrayList底层原理

服务端开发之Java备战秋招面试篇2-HashMap底层原理篇

arraylist底层原理jdk11和1.8区别

Java ArrayList底层实现原理源码详细分析Jdk8

python之路49使用set底层原理实现对象去重

集合框架