ArrayList内部实现

ArrayList是基于数组实现的,可以动态增长,它不是线程安全的,只能用于单线程,在多线程下要使用ArrayList结构,可以使用concurrent并发包下的CopyOnWriteArrayList类。

ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

ArrayList内部结构

对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。

ArrayList属性

/** 
      * The array buffer into which the elements of the ArrayList are stored. 
      * The capacity of the ArrayList is the length of this array buffer. 
      */  
     private transient Object[] elementData; //存储ArrayList元素 

     /** 
      * The size of the ArrayList (the number of elements it contains). 
      * 
      * @serial 
      */  
     private int size;  //元素数量

关键字transient: Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。

ArrayList构造器

ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。

 // ArrayList无参构造函数。默认容量是10。    
    public ArrayList() {    
        this(10);    
    }   

 // ArrayList带容量大小的构造函数。    
    public ArrayList(int initialCapacity) {    
        super();    
        if (initialCapacity < 0)    
            throw new IllegalArgumentException("Illegal Capacity: "+    
                                               initialCapacity);    
        // 新建一个数组    
        this.elementData = new Object[initialCapacity];    
    }     

    // 创建一个包含collection的ArrayList    
    public ArrayList(Collection c) {    
        elementData = c.toArray();    
        size = elementData.length;    
        if (elementData.getClass() != Object[].class)    
            elementData = Arrays.copyOf(elementData, size, Object[].class);    
    }

ArrayList实现的方法

1.存储方法

ArrayList添加元素有多种方式,包括set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection< ? extends E> c)、addAll(int index, Collection< ? extends E> c),这里只对add的两种方法进行分析,并深入了解其如何进行扩容的问题,其它方法其原理大致相似,大家可以去看源码实现。

// 将指定的元素添加到此列表的尾部。  
 public boolean add(E e) {  
    ensureCapacity(size + 1);   
    elementData[size++] = e;  
    return true;  
 }    
 // 将指定的元素插入此列表中的指定位置。  
 // 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。  
 public void add(int index, E element) {  
    if (index > size || index < 0)  
        throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);  
    // 如果数组长度不足,将进行扩容。  
    ensureCapacity(size+1);  
    // 将 elementData中从Index位置开始、长度为size-index的元素,  
    // 拷贝到从下标为index+1位置开始的新的elementData数组中。  
    // 即将当前位于该位置的元素以及所有后续元素右移一个位置。  
    System.arraycopy(elementData, index, elementData, index + 1, size - index);  
    elementData[index] = element;  
    size++;  
 } 

从源码可以看到在数组容量不够的情况下,我们需要对数组进行扩容,代码中调用了ensureCapacity方法,接下来我们分析ensureCapacity方法源码的实现。

public void ensureCapacity(int minCapacity) {
  modCount++;
  int oldCapacity = elementData.length;
  if (minCapacity > oldCapacity) {
      Object oldData[] = elementData;
      int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
    newCapacity = minCapacity;
            // 在此把整个数组copy到一个新的数组
            elementData = Arrays.copyOf(elementData, newCapacity);
  }
    }

从源码分析可以看到,ensureCapacity方法将整个数组Copy到新的数组中,这里发生第一次Copy,把原来的数组Copy到一个大约1.5倍大的新的数组里,新数组扩容的大小为:

int newCapacity = (oldCapacity * 3)/2 + 1;

第二次Copy发生在这里,index后边的数据都需要再次copy:

System.arraycopy(elementData, index, elementData, index + 1, size - index);  

2.元素读取

// 返回此列表中指定位置上的元素。  
public E get(int index) {  
    RangeCheck(index);  

    return (E) elementData[index];  
} 

3.删除元素

ArrayList提供两种删除元素方法:

删除list中指定位置的元素 remove(int index)

// 移除此列表中指定位置上的元素。  
 public E remove(int index) {  
    RangeCheck(index);  

    modCount++;  
    E oldValue = (E) elementData[index];  

    int numMoved = size - index - 1;  
    if (numMoved > 0)  
        System.arraycopy(elementData, index+1, elementData, index, numMoved);  
    elementData[--size] = null; // Let gc do its work  

    return oldValue;  
 }

通过元素对象找到此对象并删除 remove(Object o)

// 移除此列表中首次出现的指定元素(如果存在)。这是应为ArrayList中允许存放重复的元素。  
 public boolean remove(Object o) {  
    // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。  
    if (o == null) {  
        for (int index = 0; index < size; index++)  
            if (elementData[index] == null) {  
                // 类似remove(int index),移除列表中指定位置上的元素。  
                fastRemove(index);  
                return true;  
            }  
    } else {  
        for (int index = 0; index < size; index++)  
            if (o.equals(elementData[index])) {  
                fastRemove(index);  
                return true;  
            }  
        }  
        return false;  
    } 
}

注意:删除一个元素后,也可能导致该元素后面的元素发生左移。

fastRemove源码基本与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; // Let gc do its work  
 }

4.Fail-Fast机制:

ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

感谢

深入Java集合学习系列:ArrayList的实现原理

Java集合—ArrayList的实现原理

你可能感兴趣的:(Java集合类)