JDK源码/轮子分析:ArrayList原理浅解

ArrayList 个人觉得比较容易理解一点,主要就是一个正常的数组。
public class ArrayList<E>
  extends AbstractList<E>
  implements List<E>, RandomAccess, Cloneable, Serializable
{
  private static final long serialVersionUID = 8683452581122892189L;
  private static final int DEFAULT_CAPACITY = 10;
  private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
  transient Object[] elementData ;
  private int size ;
  private static final int MAX_ARRAY_SIZE = 2147483639;
以上是ArrayList 中定义的属性,默认初始化的大小为10,两个空数组,核心存储数据的数组,记录当前保存数据个数的size ,还有最大的数组大小为2^31-1-8,其中8个大小是保存一些虚拟器的头标签的。
构造函数:
public ArrayList( int paramInt )
  {
    if ( paramInt > 0) {
      this . elementData = new Object[ paramInt ];
    } else if ( paramInt == 0) {
      this . elementData = EMPTY_ELEMENTDATA ;
    } else {
      throw new IllegalArgumentException( "Illegal Capacity: " + paramInt );
    }
  }
 
参数为数组的开辟大小。  
public ArrayList()
  {
    this . elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA ;
  }
不传参则默认创建一个空数组(在你要存进数组的时候才给你开辟出大小的)
public ArrayList (Collection<? extends E> paramCollection)
  {
    this .elementData = paramCollection.toArray();
    if (( this .size = this .elementData.length) != 0)
    {
      if ( this .elementData.getClass() != [Ljava.lang.Object. class ) {
        this .elementData = Arrays.copyOf( this .elementData, this .size, [Ljava.lang.Object. class );
      }
    }
    else {
      this .elementData = EMPTY_ELEMENTDATA;
    }
  }
直接创建和指定集合一样内容的
添加数据方法:
public boolean add (E paramE )
  {
    ensureCapacityInternal( this . size + 1);
    this . elementData [( this . size ++)] = paramE ;
    return true ;
  }
不指定位置默认在数组后面添加,效率是最高的时间复杂度为O(1),添加前检查数组大小是否够用,不够用就扩容。
public void add( int paramInt , E paramE )
  {
    rangeCheckForAdd( paramInt );
    ensureCapacityInternal( this . size + 1);
    System. arraycopy ( this . elementData , paramInt , this . elementData , paramInt + 1, this . size - paramInt );
    this . elementData [ paramInt ] = paramE ;
    this . size += 1;
  }
想知道这方法干了啥,我觉得有必要弄清楚两个东西:
private void rangeCheckForAdd ( int paramInt )
  {
    if (( paramInt > this . size ) || ( paramInt < 0)) {
      throw new IndexOutOfBoundsException(outOfBoundsMsg( paramInt ));
    }
  }
1.先检查所添加数据的位置合不合理;
public static native void arraycopy(Object paramObject1 , int paramInt1 , Object paramObject2 , int paramInt2 , int paramInt3 );
这是一个本地方法,是将 paramObject1数组对象,从 paramInt1位置开始复制 paramInt3 个 paramObject2数组中,从 paramInt2这个位置开始放。
System. arraycopy ( this . elementData , paramInt , this . elementData , paramInt + 1, this . size - paramInt );
那么上面这个做的就是将  paramInt开始位置到末尾的数据整体往后移,让 paramInt这位空出来让add的值放进去。

public boolean addAll (Collection<? extends E> paramCollection )
  {
    Object[] arrayOfObject = paramCollection .toArray();
    int i = arrayOfObject . length ;
    ensureCapacityInternal( this . size + i );
    System. arraycopy ( arrayOfObject , 0, this . elementData , this . size , i );
    this . size += i ;
    return i != 0;
  }
这方法就比较容易理解了,前面是将单个数据添加到数组中去,现在是将多个数据添加到数组的末尾上去,只不过这多个数据是个集合的形式。

public boolean addAll ( int paramInt , Collection<? extends E> paramCollection )
  {
    rangeCheckForAdd( paramInt );
    Object[] arrayOfObject = paramCollection .toArray();
    int i = arrayOfObject . length ;
    ensureCapacityInternal( this . size + i );
    int j = this . size - paramInt ;
    if ( j > 0) {
      System. arraycopy ( this . elementData , paramInt , this . elementData , paramInt + i , j );
    }
    System. arraycopy ( arrayOfObject , 0, this . elementData , paramInt , i );
    this . size += i ;
    return i != 0;
  }
这是一个指定位置版的添加多个数据,可以类比指定位置的添加单个数据版的方法,在此不多说。
获取数据方法:
public E get ( int paramInt )
  {
    rangeCheck( paramInt );
    return elementData( paramInt );
  }
比较简单,因为对象是个数组,最快且有效的方法当然是直接用下标了。
另外,每个集合类都会有一个迭代器,用迭代器肯定也是可以获取数据 ……
public Iterator<E> iterator()
  {
    return new Itr( null );
  }
其中Itr是ArrayList中维护的Iterator 内部类。
我们主要看next()方法就可以知道是怎么获取数据的了。
public E next()
    {
      checkForComodification();
      int i = this . cursor ;
      if ( i >= ArrayList. this . size ) {
        throw new NoSuchElementException();
      }
      Object[] arrayOfObject = ArrayList. this . elementData ;
      if ( i >= arrayOfObject . length ) {
        throw new ConcurrentModificationException();
      }
      this . cursor = ( i + 1);
      return arrayOfObject [( this . lastRet = i )];
    }
先获取到ArrayLsit中维护的数组,再取下标(跟get中一样)。这个迭代器绕了一圈还是调用了get方法。

删除数据方法:
  public E remove ( int paramInt )
  {
    rangeCheck( paramInt );
    this . modCount += 1;
    Object localObject = elementData( paramInt );
    int i = this . size - paramInt - 1;
    if ( i > 0) {
      System. arraycopy ( this . elementData , paramInt + 1, this . elementData , paramInt , i );
    }
    this . elementData [(-- this . size )] = null ;
    return localObject ;
  }
这是根据数组下标来删除数据,很简单,将要删除的位置的后面的数据整体往前移一位即可,然后最后一位置空。最后返回你要删除的数据。
  public boolean remove (Object paramObject )
  {
    int i ;
    if ( paramObject == null ) {
      for ( i = 0; i < this . size ; i ++) {
        if ( this . elementData [ i ] == null )
        {
          fastRemove( i );
          return true ;
        }
      }
    } else {
      for ( i = 0; i < this . size ; i ++) {
        if ( paramObject .equals( this . elementData [ i ]))
        {
          fastRemove( i );
          return true ;
        }
      }
    }
    return false ;
  }
这是根据数据数据来删除,要先遍历数组找到该对象,再进行删除,这fastRemove方法跟前面的remove是一样的,不信你看
private void fastRemove ( int paramInt )
  {
    this . modCount += 1;
    int i = this . size - paramInt - 1;
    if ( i > 0) {
      System. arraycopy ( this . elementData , paramInt + 1, this . elementData , paramInt , i );
    }
    this . elementData [(-- this . size )] = null ;
  }
确实删除了数据返回true,没有则false。

修改数组数据方法:
public E set ( int paramInt , E paramE )
  {
    rangeCheck( paramInt );
    Object localObject = elementData( paramInt );
    this . elementData [ paramInt ] = paramE ;
    return localObject ;
  }
将指定下标位置中的数据替换即可,最后返回被替换的数据。

猜你喜欢

转载自blog.csdn.net/mottohlm/article/details/79067161