jdk源码——集合(ArrayList)

      昨天写了一篇文章,jdk源码——集合(一)

      我是真呵呵了,用了一天的时间,抓狂真是的,毕竟是一个新手。

      直接步入正题,今天是真真正正讲解一个我们最常用的集合ArrayList,昨天是一个准备工作。

      我查看源代码的时候,里面有很多英文注释,说实话,英文注释是精华,但是,本人英语渣渣,一般都是先把注释全部删了再看。看自己的习惯了吧!

      先看一下ArrayList的类定义,

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

     ArrayList继承AbstractList,实现了List,RandomAccess,Clonable,Serializable接口。

     我第一次看时,有一个疑惑,ArrayList继承AbstractList,为什么还要显示的声明实现List接口呢?

     在网上搜了一下,也没有一个明确的结论,好像就是说:

            1.增加可读性,明确表示实现的是List接口

            2.可以更好的兼容,如果AbstractList出现点意外

       好吧,是在编不下去了。其实ArrayList可以没有必要实现List的。

       ArrayList分别实现了

           RandomAccess---->(上一篇文章介绍过了),就是支持随机访问

           Clonable------------>,就是支持复制

           Serializable---------->,就是支持序列化和反序列化


       接下来是ArrayList类中的成员 

    private static final long serialVersionUID = 8683452581122892189L;

    private static final int DEFAULT_CAPACITY = 10; //默认初始化集合,初始化一个大小为10的数组

    static final Object[] EMPTY_ELEMENTDATA = {};//该数组是初始化时数组指定为0时,返回该数组

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认返回的是该数组

    transient Object[] elementData; // 添加transient关键字的作用的是使得该数组不被序列化,仅仅是一个数组的定义,并没有初始化,第一次添加元素时会以DEFAULT_CAPACITY的值进行扩容

    private int size;//ArrayList实际大小,因为初始化数组,有指定的值(默认为10),size是指实际想数组中添加的元素个数


       接下来是ArrayList的构造方法(三个)

    /*
    初始化一个指定大小的集合
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;//数组长度指定为0,使用的是EMPTY_ELEMENTDATA数组
        } else {
            //initialCapacity<0,报错
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        }
    }

    /*
    无参构造方法,初始化一个长度为0的集合
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//默认使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
    }

    /*
    将一个collection集合初始化为一个ArrayLIst集合
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//将集合c转变为数组
        if ((size = elementData.length) != 0) {
            // 判断集合是否为null
            if (elementData.getClass() != Object[].class)
                /*
                   Object[].class为Object,
                   elementData.getClass()也是Object,
                   如果两个相等,返回false,不相等,返回true
                   此时复制数组,要设置类型为Object类型。
                   不是一切皆为对象吗,为啥会有类型不是Object的类呢?
                 */
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;//长度为0,返回EMPTY_ELEMENTDATA
        }
    }

          

          ArrayList的常用方法

           ————添加(其中会涉及到扩容)

                 add(E e),add(int index, E element),addAll(Collection<? extends E> c),addAll(int index, Collection<? extends E> c)

  
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
      /*
      增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数
     */
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            /*判断elementData数组==DEFAULTCAPACITY_EMPTY_ELEMENTDATA,我们可以查看一下构造方法,发现,
              只有无参构造器使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
              为什么只需要判断elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,通过构造器,可以知道,
                   第一个构造器,重新创建一个住的了长度的数组
                   第三个也是一个新数组或者是指定为长度为0的数组
              */
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//选取默认值10和传入的参数中大的值作为最小容量
            /*
               此时,new集合时,调用构造方法是,并不会为底层数组指定大小,
               只有add是才会为数组指定参数,
                 数组为为空时,将数组初始化为10
                 数组不为空时,数组长度和10比较,选取大值,也就是说  size>0 size<10,将数组初始化长度为10
                 size>10,则改变长度
             */
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//操作+1
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


    /*
    扩容操作
     */
    private void grow(int minCapacity) {
        //数组扩容
        int oldCapacity = elementData.length;//获得elementData的实际长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容实际长度的1.5倍
        if (newCapacity - minCapacity < 0)//扩容1.5倍还是不够,按最小容量扩容
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //如果newCapacity太大
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//数组复制
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // 判断minCapacity是不是<0
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE ://确实是大于,则设置为Integer对象所能表示的最大值
                MAX_ARRAY_SIZE;//如果不是,就按MAX_ARRAY_SIZE
    }
    
    
    /*
   list中最重要的方法add(),1.确定list容量,尝试容量+1
    */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //添加元素,数组长度size+1,判断数组是否满足可以添加元素的条件(数组容量>size+1)
        elementData[size++] = e;//此时size++
        return true;
    }

    /*
    将指定的元素插入此列表中的指定位置
     */
    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++;
    }
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;
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);//判断下标是否越界

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // 是否需要扩容

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                    numMoved);//复制并移动元素,该方法是一个本地方法

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }

    ————删除

 
 
  //就是数组根据下标获得值
   E elementData(int index) {
        return (E) elementData[index];
    }
   public E remove(int index) {
        rangeCheck(index);//index>=size,会抛异常
        modCount++;
        E oldValue = elementData(index);//获得elementData数组下标为index的值

        int numMoved = size - index - 1;//下标从0开始,要-1
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);//本地方法,移动数组,删除后肯定要重新进行整理,删除的元素后面要重新补到前面
        elementData[--size] = null; //把后面空着的位置设置为null

        return oldValue;
    }


    public boolean remove(Object o) {
        if (o == null) {
            //需要判断null,有可能元素就是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;
    }

    /*
      这个方法,其实和remove(int 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; // clear to let GC do its work
    }

    /*
    包内可以访问,移除指定范围内的元素,调用的也是一个本地方法,删除,仅仅是将元素变为null,让后改变size
      a b c d e
      0 1 2 3 4  size=5, fromIndex=1 toIndex=3
     */
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }



   

       ————修改

//就是指的替换
public E set(int index, E element) {
        rangeCheck(index);

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


         ———— 获取

   public E get(int index) {
        rangeCheck(index);//仅仅是判断该参数是否合法

        return elementData(index);
    }


     ———— 其他方法

         size(),获得集合长度:

public int size() {
        return size;
    }

          isEmpty(),判断集合是否为空:

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

        indexOf(Object o),获得首次出现该对象的索引值:

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

        lastindexOf(Object o),获得最后一次出现该对象的索引值:

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

      clone(),返回一个数组的与该数组一样的数组,浅复制:

    /*
    因为所有类的父类都是Object,源码中Object类中有一个clone()方法
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();//调用父类的clone(),其实克隆了一个空数组
            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(e);
        }
    }

       toArray()

    /*
      按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
    */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
        //就是数组的复制,返回的是一个新的数组,即使改变这个数组,原来的数组不会改变
    }

    @SuppressWarnings("unchecked")
    /*
    按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型
    就是讲集合中的元素方法a数组中
     */
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // 如果a的长度小于集合的长度,复制elementData数组即可并返回,一般只是让一个集合转化为有类型的数组
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());//该数组的复制,有具体的类型
        System.arraycopy(elementData, 0, a, 0, size);//也是复制将elementData复制到a
        if (a.length > size)
            a[size] = null;//将size设置为null
        return a;
    }

        clear(),移除所有元素:

 public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            elementData[i] = null;//循环遍历,元素设置为null
        size = 0;//长度设置为0
    }

        去除指定集合的元素和取出两个集合的交集:removeAll(),retainAll()

     /*
      移除包含有指定集合的元素
     */
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    /*
    取两个集合的交集,改变的是集合本身
     */
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }
    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];//W++,先复制后计算,将后面的值放置到前面
                    //removeAll和retainAll因为complement参数的不同而有所不同,
                    /*
                        removeAll ,complement为false,不包含,进入
                        retainAll ,complement为true,包含,进入
                     */
        } finally {
            if (r != size) {
                //如过r!=size,可能try语句报错,elementData数组中所有元素并没有完全判断,将没有判断的元素,从w开始复制到到后面
                System.arraycopy(elementData, r,
                        elementData, w,
                        size - r);
                w += size - r;//w=w+size-r;
            }
            if (w != size) {
                //将w后面的位置设置为null,便于垃圾收集器继续处理
                for (int i = w; i < size; i++)
                    //改变数组的实际大小
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
好了,ArrayList的源码已经分析完了,这都是自己的拙见,有不同的看法,请告诉我哦!



猜你喜欢

转载自blog.csdn.net/qq_36838854/article/details/80302919