java集合框架-ArrayList

  虽然在源码实现上jdk1.7和jdk1.8有所不同,但是ArrayList底层维护的依然是一个动态数组,每个ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。

      ArrayList不是同步的(也就是说不是线程安全的),如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步,在多线程环境下,可以使用Collections.synchronizedList方法声明一个线程安全的ArrayList,但是这种方法并不一定是安全的(比较复杂的一种情况),所以一般不用,例如:

      List arraylist = Collections.synchronizedList(new ArrayList());

      下面通过ArrayList的源码来分析其原理,基于jdk1.8。

      1、ArrayList的构造方法:ArrayList提供了三种不同的构造方法

      1) ArrayList(),将elementData指向一个空对象数组,事实上在add方法的时候会做第一次初始化容量为10

      2) ArrayList(int initialCapacity),构造一个具有指定初始容量的空列表。

      3) ArrayList(Collection<? extends E> c),构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

      源码如下:

  

1
private  transient  Object[] elementData;
private static final Object DEFAULTCAPACITY_EMPTY_ELEMENTDATA[] = new Object[0];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  ArrayList( int  initialCapacity) {
     if(i>0)
elementData = new Object[i]; //初始化数组
     else if(i == 0)
            elementData = EMPTY_ELEMENTDATA;
          else
            throw new IllegalArgumentException((new StringBuilder()).append("Illegal Capacity: ").append(i).toString());
}
   public  ArrayList() {
    elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }
   public  ArrayList(Collection<?  extends  E> c) {
        elementData = c.toArray();    //返回包含此 collection 中所有元素的数组
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if  (elementData.getClass() != Object[]. class )
       elementData = Arrays.copyOf(elementData, size, Object[]. class );  //复制指定的数组,返回包含相同元素和长度的Object类型的数组
   }

  当采用不带参数的构造方法ArrayList()生成一个集合对象时,其实是在底层调用ArrayList(int initialCapacity)这一构造方法生产一个长度为10的Object类型的数组。当采用带有集合类型参数的构造方法时,在底层生成一个包含相同的元素和长度的Object类型的数组。

 

     2、add方法:ArrayList提供了两种添加元素的add方法

     1) add(E e),将指定的元素添加到此列表的尾部。

     2) add(int index, E e),将指定的元素插入此列表中的指定位置。向右移动当前位于该位置的元素(如果有)以及所有后续元素(将其索引加 1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  boolean  add(E e) {
     ensureCapacity(size +  1 );   // 扩大数组容量
     elementData[size++] = e;    //将元素e添加到下标为size的Object数组中,并且执行size++
     return  true ;
     }
public  void  add( int  index, E element) {
     if  (index > size || index <  0 )   //如果指定要插入的数组下标超过数组容量或者指定的下标小于0,抛异常
         throw  new  IndexOutOfBoundsException( "Index: " +index+ ", Size: " +size);
 
     ensureCapacity(size+ 1 );   // 扩大数组容量
     System.arraycopy(elementData, index, elementData, index +  1 ,size - index);   //从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。<br>                                                                                    // elementData --- 源数组   index --- 源数组中的起始位置   <br>                                                                                    // elementData --- 目标数组  index+1 ---  目标数组中的起始位置<br>                                                                                    // size - index --- 要复制的数组元素的数量
     elementData[index] = element;  //将要添加的元素放到指定的数组下标处
     size++;
     }
1
2
3
4
5
6
7
8
9
10
11
12
public  void  ensureCapacity( int  i) {
   byte byte0 = ((byte)(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA?10:0))//如果是第一次add,则容量为10
  if(i > byte0)
ensureExplicitCapacity(i);
}
private void ensureExplicitCapacity(int i){
  modCount++;
    int  oldCapacity = elementData.length;   //原数组的容量
    if  (i > oldCapacity) {
        Object oldData[] = elementData;
        int  newCapacity = (oldCapacity *  3 )/ 2  1 ;   //定义新数组的容量,为原数组容量的1.5倍+1
            if  (newCapacity < minCapacity)
        newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);   //复制指定的数组,返回新数组的容量为newCapacity
    }
    }

  如果集合中添加的元素超过了10个,那么ArrayList底层会新生成一个数组,长度为原数组的1.5倍+1,并将原数组中的元素copy到新数组中,并且后续添加的元素都会放在新数组中,当新数组的长度无法容纳新添加的元素时,重复该过程。这就是集合添加元素的实现原理。

 

     3、get方法:

     1) get(int index),返回此列表中指定位置上的元素。

     

1
2
3
4
5
6
7
8
9
10
11
public  E get( int  index) {
     RangeCheck(index);  //检查传入的指定下标是否合法
 
     return  (E) elementData[index];   //返回数组下标为index的数组元素
     }
 
private  void  RangeCheck( int  index) {
     if  (index >= size)   //如果传入的下标大于或等于集合的容量,抛异常
         throw  new  IndexOutOfBoundsException(
         "Index: " +index+ ", Size: " +size);
     }

  

     4、remove方法:

     1) E remove(int index),移除此列表中指定位置上的元素。向左移动所有后续元素(将其索引减 1)。

     2) boolean remove(Object o),移除此列表中首次出现的指定元素(如果存在)。如果列表不包含此元素,则列表不做改动,返回boolean值。

     

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
     }
 
  public  boolean  remove(Object o) {
     if  (o ==  null ) {  //如果传入的参数为null
             for  ( int  index =  0 ; index < size; index++)
         if  (elementData[index] ==  null ) {   //移除首次出现的null
             fastRemove(index);
             return  true ;
         }
     else  {
         for  ( int  index =  0 ; index < size; index++)
         if  (o.equals(elementData[index])) {
             fastRemove(index);
             return  true ;
         }
         }
     return  false ;
     }
 
private  void  fastRemove( int  index) {   //移除指定位置的元素,实现方法类似remove(int i)
         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
     }

 

    5、clone方法:

    1) Object clone(),返回此ArrayList实例的浅表副本(不复制这些元素本身) 。

    

1
2
3
4
5
6
7
8
9
10
11
public  Object clone() {
    try  {
        ArrayList<E> v = (ArrayList<E>)  super .clone();   //调用Object类的clone方法返回一个ArrayList对象
        v.elementData = Arrays.copyOf(elementData, size);   //复制目标数组
        v.modCount =  0 ;
        return  v;
    catch  (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw  new  InternalError();
    }
    }

  

    以上通过对ArrayList部分关键源码的分析,知道了ArrayList底层的实现原理,关于ArrayList源码有以下几点总结:

    1) ArrayList 底层是基于数组来实现的,可以通过下标准确的找到目标元素,因此查找的效率高;但是删除元素会涉及到大量元素的位置移动,效率低。

    2) ArrayList提供了三种不同的构造方法,当集合中添加的元素个数大于10,数组会自动进行扩容,即生成一个新的数组,并将原数组的元素放到新数组中。

    3) ensureCapacity方法对数组进行扩容,它会生成一个新数组,长度是原数组的1.5倍+1,随着向ArrayList中不断添加元素,当数组长度无法满足需要时,重复该过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值