码迷,mamicode.com
首页 > 其他好文 > 详细

jdk 1.7 ArrayList 源码分析

时间:2015-08-28 00:42:19      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:

1、首先看看ArrayList 的基类有哪些

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

可以总结为:

  ArrayList 继承了AbstractList 

               实现了 List 、 RondomAcess 、 cloneable 、 serializable 对于一般的容器都会实现cloneable 用于克隆 实现serializable 实现序列化

 

2、在ArrayList中定义的变量

主要有4个变量,在了解这些变量之前我们需要知道一些基本信息,ArrayList 底层用的是数组实现的,初始的默认容量是10 ,

private static final int DEFAULT_CAPACITY = 10;//初始的默认容量

private static final Object[] EMPTY_ELEMENTDATA = {}; //空数组

private transient Object[] elementData;//ArrayList 中真正存数据的容器

private int size;//当前数组中的数据个数

 

3、ArrayList中的构造函数

ArrayList中总共有三个构造函数,分别是有初始化容量的构造函数,没有参数的构造函数,带有集合参数的构造函数

(1) 从源码中我们可以看到这只是一个很简单的 创建数组对象的过程,不过在创建对象之前需要对初始容量值判断是否<0 

public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];  
}

(2)无参的构造函数,这里我们需要注意的是虽然数组默认容量是10 ,但是无参的情况下数组的初始化实际是个空数组而不是创建大小为10的数组
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}

(3)集合 因为是对数组操作所以 用的是Arrays.copyOf 进行复制和类型转换
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}

 

4、主要函数的分析 对于容器一般我们常用的操作是增删改查 

(1)增加一个数的操作,size记录当前数组中的数个数所以实际操作是在数组下标为size位置赋值,然后 size++ ,但是在这之前得判断size+1是否越界,也就是是否需要进行扩容,扩容我们下面再讲,该函数的返回值是Boolean类型

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

 

(2)在指定的下标增加一个数的操作。在这里我们需要注意一个函数就是System.arraycopy()这个是ArrayList中常用的API,主要用于数组移位

原先的数存在elementData[]中,一共size个对象,现在我们需要在下标为index插入新对象 就需要我们将 下标[index ,size-1]范围的对象移位到[index+1,size]

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

                                      (源数组 ,复制开始下标,目标数组,目标数组开始下标,复制对象的个数)     

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

(3)增加一个集合的操作。首先将集合转换为数组,再判断假如该集合数目的对象是否需要扩容,再调用System.arraycopy函数将新数组中的数复制到elementData[]中

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

 

(1)删除下标为index的对象,先判断index是否有效,然后是向前移一位复制,最后size下标位置赋值为null,返回删除的对象

public E remove(int index) {
      rangeCheck(index);

      modCount++;
      E oldValue = elementData(index);//这里可能会疑惑为什么是elementData(index)而不是elementData[index] 因为elementData(index)是ArrayList的一个内部函数,实际也是返回下标为index的对象。

      int numMoved = size - index - 1;
      if (numMoved > 0)
      System.arraycopy(elementData, index+1, elementData, index,numMoved);// 又出现了System.arraycopy。。。。当别人问我ArrayList中对什么印象最深刻那么就是这个了
      elementData[--size] = null; // clear to let GC do its work

     return oldValue;
}

(2)删除某个对象操作。实际上这是个遍历数组顺便比较的过程,分两个条件去遍历,当删除的对象是 null的情况可非null的情况下进行,从中我们可以看出实际上在遇到第一个符合条件的对象就返回了,所以这个操作并不删除完所有

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

 

(3) 删除一个集合的操作。

public boolean removeAll(Collection<?> c) {
          return batchRemove(c, false);// 调用一个内部函数
}

//这个函数利用了tow poit 的思想,一个读节点,一个写节点,遍历数组当该对象不存在集合中时保留下来,最后对[w,size]范围赋值为null

private boolean batchRemove(Collection<?> c, boolean complement) {
     final Object[] elementData = this.elementData;
     int r = 0, w = 0;
     boolean modified = false;
     try {
     for (; r < size; r++)
           if (c.contains(elementData[r]) == complement)
           elementData[w++] = elementData[r];
      } finally {
     // Preserve behavioral compatibility with AbstractCollection,
     // even if c.contains() throws.
     if (r != size) {
            System.arraycopy(elementData, r,elementData, w,size - r);
            w += size - r;
      }
     if (w != size) {
     // clear to let GC do its work
           for (int i = w; i < size; i++)
           elementData[i] = null;
           modCount += size - w;
           size = w;
           modified = true;
      }
      }
      return modified;
}

(1) 对index下标处的对象赋值新的对象,首先判断index是否有限,然后对数组下标为index位置赋值,返回旧对象

public E set(int index, E element) {
     rangeCheck(index);

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

 

(1)查找下标为index 的对象 ,先判断 index是否有效,再返回下标为index的数组

public E get(int index) {
      rangeCheck(index);

      return elementData(index);
}

扩容分析

上面我们在分析增加一个对象操作之前会调用 ensureCapacityInternal(size + 1) ,现在我们将沿着这条线去分析扩容的过程

private void ensureCapacityInternal(int minCapacity) {
       if (elementData == EMPTY_ELEMENTDATA) {
             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//当当前数组为空时取默认容量与最小容量的最大值
       }

      ensureExplicitCapacity(minCapacity);//用来比较当前的数组长度和最小容量
}

 

private void ensureExplicitCapacity(int minCapacity) {
      modCount++;

     // overflow-conscious code
      if (minCapacity - elementData.length > 0)//当需要的最小容量大于当前数组的长度时需要进行扩容
           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倍旧容量与最小需要的容量比较取较大值
              newCapacity = minCapacity;
      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);//创建新newCapacity的长度为数组将旧数组的值复值给新数组
}

 

private static int hugeCapacity(int minCapacity) {
       if (minCapacity < 0) // overflow
           throw new OutOfMemoryError();
       return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE; //从这里可以看出数组能够允许的最大扩容大小为Integer.MAX_VALUE
}

 

其他常用的函数

1、判空函数

public boolean isEmpty() {
      return size == 0;
}

2、查当前ArrayList中的对象个数

public int size() {
      return size;
}

3、是否含有某个对象

public boolean contains(Object o) {
       return indexOf(o) >= 0;
}

//同样是分为null 和非 null 去遍历

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

4、将容量缩小 , 减少空间

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
          elementData = Arrays.copyOf(elementData, size); //创建size大小的新数组,将旧数组的值赋值到新数组
    }
}

 

总结: ArrayList 底层用的是数组实现,默认初始化容量为10 ,扩容规则为旧数组长度的1.5倍与当前需要的最小容量的最大值,最常用的操作是Arrays.copyOf() 和  System.arraycopy() 

技术分享
技术分享
技术分享

jdk 1.7 ArrayList 源码分析

标签:

原文地址:http://www.cnblogs.com/sophelia-M/p/4765152.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!