您的位置:首页 > 编程语言 > Java开发

Java集合JDK1.7(源码分析)之ArrayList

2018-03-27 21:12 399 查看
第一部分:ArrayList介绍
        ArrayList是一个动态的数组,与java的数组相比较,java数组的长度在定义的时候或者是通过复制形式创建时已经确定了其容器的大小,而ArrayList的容器大小是动态增长的。



    查看JDK1.7源码我们知道ArrayList类继承AbstractList这个抽象类,并且还实现了RandomAccess、Cloneable、SerialIzable接口。当然ArrayList对List集合的实现最重要的是继承了AbstractList,而RandomAccess、Cloneable、SerialIzable只是一些标记性接口:
1、ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。
2、ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
3、ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
     ArrayList是一个动态数组,实现了List。提供了添加、删除、修改、遍历等相关操作功能。但是和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList(JUC包下面提供并发访问的容器类)。
第二部分:ArrayList源码之变量
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

{
  //类的版本控制的序列号
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    //定义的默认容纳大小
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
  //定义的Object类型的空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;

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

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */

第三部分:ArrayList源码之构造函数

   1、无参构造,如果没有传递就把private static final Object[] EMPTY_ELEMENTDATA = {};该对象复制给数组对象
       创建一个空的数组,它会在后面的add方法中从新初始化数组的大小为默认值10



   2、有参构造传入指定的容量大小



3、根据传入的集合大小和数据创建



 
 其实刚看第一个无参数的构造函数的时候觉得很纳闷,因为赋值一个(EMPTY_ELEMENTDATA = {})空的数组给我们底层存储数据的(Object [] elementData)对象,那么我们还能添加进去数据吗?答案是能,不然平时我们都是怎么添加进去的,这就涉及到对ArrayList底层存储之数组的容量大小的扩容问题。当然jdk1.6和jdk1.7在这个策略上有细微差别就不阐述了。jdk1.7扩容策略主要看ArrayList的add(E)添加元素方法。
 第三部分:ArrayList源码之ArrayList的方法

 1、 ArrayList的add(E e)方法   
    



2、//扩容策略过度方法  (当初始化无参数构造时候minCapacity==1);不是调用无参数刚初始化的时候就是原来集合大小+1
    private void ensureCapacityInternal(int minCapacity) {
            //判断是不是刚初始化出来的
          if (elementData == EMPTY_ELEMENTDATA) {
            //获取默认值10和传入值比较取最大那个
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
          }

        //继续调用处理
        ensureExplicitCapacity(minCapacity);
    }
3、  //扩容策略过度方法 

 private void ensureExplicitCapacity(int minCapacity) {
   //抽线父类的统计变量,没啥用
        modCount++;
  // overflow-conscious code
         //重要代码  通过计算看是否需要扩容,小于零就不需要了,大于零就需要(表示容器不够了)
        if (minCapacity - elementData.length > 0)
             //真正的扩容策略函数(扩容方式)
            grow(minCapacity);
    }
 4、扩容策略函数grow(int minCapacity )

       private void grow(int minCapacity) {
        //原来数组长度,刚初始化时候为零
        // overflow-conscious code
        int oldCapacity = elementData.length;
       //扩容核 (有移原来数组长度1位;表示取原来数组长度一般+原来数组长度最为新数组的容量大小)  
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
         //刚初始化newCapacity为0,minCapacity为1  0-1<0
         //所以刚初始化时候容器的默认长度为10(定义的常量)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //将原来数组中的数据拷贝到新的数组中,容量大小为原来的1/2倍。刚初始时候大小为10
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  4、Arrays.copyOf()方法介绍
  
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
如果 newType 是一个对象对组,就直接把 original 的元素拷贝到 对象数组中;

否则新建一个 newType 类型的数组。
 5、往ArrayList指定位置添加元素add(int index,E element)

     public void add(int index, E element) {
      // 检查有没有超出范围,有就抛出异常IndexOutOfBoundsException,没有就看看需不需要扩容
         rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
6、rangeCheckForAdd方法为检查添加的元素的位置是不是在正常的范围内
   private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(inde
a5d6
x));
    }
7、是否包含某个元素contains(Object o)
  public boolean contains(Object o) {
        return indexOf(o) >= 0;

    }   
8、返回对应元素从前往后第一次出现的索引位置int  indexOf(Object 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++)
              //通过传入对象的equals的方法进行比较
             //如果对象没有重写Object对象的equals方法那么是==
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
8、Object类对象的boolean  equals(Object o)方法是使用==作为判断依据
      public boolean equals(Object obj) {
        return (this == obj);

    }
9、返回Object对象在数组中最后一次出现的索引位置 int  lastIndexOf(Object) 没有找到返回-1
   public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
 10、E get(int index):通过索引位置获取集合类元素 

    public E get(int index) {
      //检出是否有异常
        rangeCheck(index);
        return elementData(index);
    }
11、检出get获取时候的异常函数
 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }
12、E set(int index,E element)给指定位置添加元素
  public E set(int index, E element) {
        rangeCheck(index);

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

    }
12、E remove(int index)删除指定位置的元素
//根据位置删除
public E remove(int index) {
rangeCheck(index);

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

//挨个往前移一位
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

return oldValue;
}

13、删除某个元素
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: