您的位置:首页 > 其它

深入探讨源码--ArrayList

2020-12-12 21:40 337 查看

目录 

深入探讨源码之

ArrayList 
ArrayList 
类图
ArrayList
的数据结构
ArrayList
的关键属性
ArrayList
构造方法
ArrayList
常用方法add方法
ArrayList
中的
fast-fail
机制add(i,o)方法set(i,o)方法get(i)方法remove(index)方法remove(Object)方法clear方法
indexOf(o)
方法

深入探讨源码之
ArrayList

java.util.ArrayList
位于
JDK
的rt.jar中;出生于
JDK1.2
。本文
JDK
版本基于
JDK1.8

List接口的可调整大小的数组实现,实现了所有可选的List操作,允许所有数据都为null。类Vector类似,

ArrayList
是线程不安全的,Vector是线程安全的。

ArrayList
类图

上面这张图基本上描述的很清晰了,实现了四个接口一个抽象类。它继承了

AbstractList
抽象类,实现了List、
RandomAccess
Cloneable
,
Serializable
接口。

  • 它继承于

    AbstractList
    ,实现了
    List
    RandomAccess
    随机访问,
    Cloneable
    可克隆, 
    java.io.Serializable
    序列化]这些接口。

  • ArrayList
    继承了
    AbstractList
    ,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。

  • ArrayList
    实现了
    RandmoAccess
    接口,即提供了随机访问功能。

  • ArrayList
    实现了
    Cloneable
    接口,即覆盖了函数clone(),能被克隆。

  • ArrayList
    实现
    java.io.Serializable
    接口,这意味着
    ArrayList
    支持序列化,能通过序列化去传输。

  • 和Vector不同,

    ArrayList
    中的操作不是线程安全的。所以,建议在单线程中才使用
    ArrayList
    ,而在多线程中可以选择Vector或者
    CopyOnWriteArrayList

ArrayList
的数据结构

数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。

ArrayList
的数据结构是:

说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对

ArrayList
类的实例的所有的操作底层都是基于数组的。

ArrayList
的关键属性

//初始化大小为10private static final int DEFAULT_CAPACITY = 10;//空对象数组private static final Object[] EMPTY_ELEMENTDATA = {};//缺省空对象数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//存数据的数组transient Object[] elementData;//当前ArrayList的大小private int size;//ArrayList最大容量//Integer.MAX_VALU=(2的31次方)-1private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList
构造方法

//开发人员使用最多的构造方法,全部使用默认参数public ArrayList() {  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}//部分开发人会用的构造方法,可以指定初始大小的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(Collection<? extends E> c) {    elementData = c.toArray();    if ((size = elementData.length) != 0) {      // c.toArray might (incorrectly) not return Object[] (see 6260652)       if (elementData.getClass() != Object[].class)         elementData = Arrays.copyOf(elementData, size, Object[].class);     } else {          // replace with empty array.          this.elementData = EMPTY_ELEMENTDATA;    }}

ArrayList
常用方法

add方法

ArrayList
中添加元素,使用demo

public class ArrayListDemo {   public static void main(String[] args) {       ArrayList<String> arrayList=new ArrayList<>();       arrayList.add("Java后端技术栈");       arrayList.add("tian");       arrayList.add("Java");       arrayList.add("DB");       Iterator<String> mIterator = arrayList.iterator();       while (mIterator.hasNext()){           if ("tian".equals(mIterator.next())){               arrayList.add("C");           }       }   }}

add方法源码分析

public boolean add(E e) {     //确保内部容量,size为elementData的长度,本次添加一个     //即size+1     //使用第一个构造方法,第一次添加数据的时候,size=0     ensureCapacityInternal(size + 1);  // Increments modCount!!     elementData[size++] = e;     return true;}private void ensureCapacityInternal(int minCapacity) {     //elementData是空数组,minCapacity=0+1=1     ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {     //两个数组都是空的,所以这里是相等的     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        //返回DEFAULT_CAPACITY=10和minCapacity=1比较,谁大返回谁         //那么这里就返回10        return Math.max(DEFAULT_CAPACITY, minCapacity);     }     return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {     //用来标记当前arraylist集合操作变化的次数     modCount++;     //如果minCapacity>elementData.length     //minCapacity=10,elementData.length=0     if (minCapacity - elementData.length > 0){           grow(minCapacity);     }}private void grow(int minCapacity) {   //将扩充前的elementData大小给oldCapacity   int oldCapacity = elementData.length;   //newCapacity就是1.5倍的oldCapacity   //第一次添加元素的时候,newCapacity依旧是0   int newCapacity = oldCapacity + (oldCapacity >> 1);   //这句话就是适应于elementData就空数组的时候,length=0,   //那么oldCapacity=0,newCapacity=0,所以这个判断成立,   //在这里就是真正的初始化elementData的大小了,   //就是为10.前面的工作都是准备工作。   if (newCapacity - minCapacity < 0){           newCapacity = minCapacity;   }   //如果newCapacity超过了最大的容量限制,就调用hugeCapacity,   //也就是将能给的最大值给newCapacity   //比较一下newCapacity与(2的31次方)-1谁大   if (newCapacity - MAX_ARRAY_SIZE > 0){           newCapacity = hugeCapacity(minCapacity);   }   // minCapacity is usually close to size, so this is a win:   //新的容量大小已经确定好了,就copy数组,改变容量大小。   elementData = Arrays.copyOf(elementData, newCapacity);}//OOM或者就是赋值最大容量private static int hugeCapacity(int minCapacity) {   if (minCapacity < 0) // overflow         throw new OutOfMemoryError();   //如果minCapacity都大于MAX_ARRAY_SIZE,那么就Integer.MAX_VALUE返回,   //反之将MAX_ARRAY_SIZE返回。因为maxCapacity是三倍的minCapacity,   //可能扩充的太大了,就用minCapacity来判断了。   //Integer.MAX_VALUE:2147483647   MAX_ARRAY_SIZE:2147483639   //也就是说最大也就能给到第一个数值。还是超过了这个限制,   //就要溢出了。相当于arraylist给了两层防护。   return (minCapacity > MAX_ARRAY_SIZE) ?      Integer.MAX_VALUE : MAX_ARRAY_SIZE;}
ArrayList
中的
fast-fail
机制

运行上面demo

还记得上面源码中

ensureExplicitCapacity
方法中有个
modCount
变量么?

抛出的异常正是我们刚才提到的

ConcurrentModificationException
 并发修改异常,正是由于fail-fast机制导致的。在什么情况下会出现该异常呢?我先简单说下,一般情况下有两种情况,一种情况发生在多线程操作,当a线程正在通过迭代器操作集合
arrayList
时,同时b线程对
arrayList
进行添加或者删除元素,会触发fail-fast机制,抛出该异常。另一种情况是在迭代集合
arrayList
的过程中对
arrayList
集合进行元素添加或者删除操作时,会触发fail-fast机制,抛出该异常。归根结底就是当我们进行集合数据迭代时,有其他操作修改了当前
Arraylist
modCount
的值,导致
modCount != expectedModCount
,会抛出该异常。

public Iterator<E> iterator() {    return new Itr();}private class Itr implements Iterator<E> {    // next 元素角标    int cursor;       // index of next element to return    // last 元素角标    int lastRet = -1; // index of last element returned; -1 if no such    int expectedModCount = modCount;       public E next() {           //检查共变性           checkForComodification();           int i = cursor;           if (i >= size)               throw new NoSuchElementException();           Object[] elementData = ArrayList.this.elementData;           if (i >= elementData.length)               throw new ConcurrentModificationException();           cursor = i + 1;           return (E) elementData[lastRet = i];    }   final void checkForComodification() {           //当modCount != expectedModCount时,           //会抛出ConcurrentModificationException异常           if (modCount != expectedModCount)               throw new ConcurrentModificationException();   }}

add、remove、clear元素都会使得

modCount
变化。

add(i,o)方法

添加到指定的位置。

使用demo

public class ArrayListDemo1 {   public static void main(String[] args) {       ArrayList<String> arrayList=new ArrayList<>();       arrayList.add(1,"Java后端技术栈");   }}

具体源码

//index为即将要存放数据的位置,element为数据public void add(int index, E element) {       //添加之前,先检查index是否满足条件       rangeCheckForAdd(index);       //前面已经分析了       ensureCapacityInternal(size + 1);  // Increments modCount!!       //将index及其后边的所有的元素整块后移,空出index位置       System.arraycopy(elementData, index, elementData, index + 1,                        size - index);       //将元素存放在index位置上       elementData[index] = element;       //数组大小加1       size++;}private void rangeCheckForAdd(int index) {       //如果使用第一种构造方法,那么size==0       //如果index>size或者index<0就会抛数组越界异常       if (index > size || index < 0)           throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}

运行demo,这个方法慎用,平时工作使用的不是很多,因为在使用的时候,需要保证不满足

index > size和Index<0中的任何一个条件,否则将出现:

set(i,o)方法

使用demo

public class ArrayListDemo1 {   public static void main(String[] args) {       ArrayList<String> arrayList=new ArrayList<>();       arrayList.add("Java后端技术栈");       arrayList.add("tian");       arrayList.add("Java");       arrayList.set(1,"C");       for (String string:arrayList){           System.out.println(string);       }   }}

源码

//把Index位置上的旧值拿出来,然后把新值放进去public E set(int index, E element) {       rangeCheck(index);       //获取index位置上的旧值       E oldValue = elementData(index);       //把index位置上设置新值       elementData[index] = element;       //返回旧值       return oldValue;}//index大于ArrayList的大小,就明显是数组越界了。private void rangeCheck(int index) {       if (index >= size)           throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}
get(i)方法

获取数据

//检查index是否越界,然后获取index上的数据public E get(int index) {       rangeCheck(index);       return elementData(index);}E elementData(int index) {       return (E) elementData[index];}
remove(index)方法
   public E remove(int index) {       //范围检查       rangeCheck(index);       //modCount加1       modCount++;       //取出旧值       E oldValue = elementData(index);       //获取从哪个下标开始移动数据       int numMoved = size - index - 1;       //如果删除的不是最后一个元素       if (numMoved > 0)           //删除的元素到最后的元素整块前移           System.arraycopy(elementData, index+1, elementData, index,                            numMoved);       //将最后一个元素设为null,方便垃圾回收集在下次gc的时候就会回收掉了       elementData[--size] = null; // clear to let GC do its work       return oldValue;   }
remove(Object)方法

和remove(index)雷士,只是多了一层遍历而已。性能明细没有remove(index)好。

   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;   }  //和前面remove(index) 类似了   private void fastRemove(int index) {       modCount++;       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   }
clear方法
public void clear() {       modCount++;       // clear to let GC do its work       //清楚就是把ArrayList中的所有对象置为null,方便垃圾收集器收集       for (int i = 0; i < size; i++)           elementData[i] = null;        //最后把size置为0        size = 0;}
indexOf(o)
方法

获取o所在的下标,如果ArrayList中没有o,那么就返回 -1;

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: