Java SE——ArrayList

一ArrayList定义
在这里插入图片描述
1 实现RandomAccess接口:
这也是一个标记接口,接口中没有任何方法和常量,表名该类支持快速的随机访问。在工具类Collections
中应用二分查找方法时判断了是否实现了该接口

1     int binarySearch(List<? extends Comparable<? super T>> list, T key) {
2         if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
3             return Collections.indexedBinarySearch(list, key);
4         else
5             return Collections.iteratorBinarySearch(list, key);
6     }

2 实现Cloneable接口:
浅拷贝可以通过调用Object.clone();但是调用该方法的对象一定要实现该接口,否则会抛出CloneNoSupportException。
3 实现了Serializable接口:
表示可以序列化
4 实现list接口:
list接口是list集合类的上层接口,实现该接口的类都必须实现一些方法
在这里插入图片描述
二 字段属性:

        //集合的默认大小
        private static final int DEFAULT_CAPACITY = 10;
        //空的数组实例
        private static final Object[] EMPTY_ELEMENTDATA = {};
        //这也是一个空的数组实例,和EMPTY_ELEMENTDATA空数组相比是用于了解添加元素时数组膨胀多少
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        //存储 ArrayList集合的元素,集合的长度即这个数组的长度
        //1、当 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时将会清空 ArrayList
        //2、当添加第一个元素时,elementData 长度会扩展为 DEFAULT_CAPACITY=10
        transient Object[] elementData;
        //表示集合的长度
        private int size;

三 构造函数:
在这里插入图片描述
  此无参构造函数将创建一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA 声明的数组,注意此时初始容量是0,而不是大家以为的 10。
  注意:根据默认构造函数创建的集合,ArrayList list = new ArrayList();此时集合长度是0.
  在这里插入图片描述
  创建一个指定大小的数组,等于0时,就创建一个空数组,小于0时,抛出IllegalArgumentException异常。
  在这里插入图片描述
 将已有的集合复制到ArrayList中。
 四 方法:
 在这里插入图片描述 通过前面的字段属性和构造函数,我们知道 ArrayList 集合是由数组构成的,那么向 ArrayList 中添加元素,也就是向数组赋值。我们知道一个数组的声明是能确定大小的,而使用 ArrayList 时,好像是能添加任意多个元素,这就涉及到数组的扩容。
  扩容的核心方法就是调用Arrays.copyOf 方法,创建一个更大的数组,然后将原数组元素拷贝过去即可。下面我们看看具体实现。
  如上图所示,增加元素之前会调用ensureCapacityInternal()方法来确定集合的大小,如果集合满了,就要扩容操作。

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
        //这里的minCapacity 是集合当前的大小+1,可以看add的传参
    private void ensureCapacityInternal(int minCapacity) {  
        //elementData 是实际用来存储元素的数组,注意数组的大小和集合的大小不是相等的,前面的size是指集合大小
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

接下来就是扩容的方法:

 1     private void grow(int minCapacity) {
 2         int oldCapacity = elementData.length;//得到原始数组的长度
 3         int newCapacity = oldCapacity + (oldCapacity >> 1);//新数组的长度等于原数组长度的1.5倍
 4         if (newCapacity - minCapacity < 0)//当新数组长度仍然比minCapacity小,则为保证最小长度,新数组等于minCapacity
 5             newCapacity = minCapacity;
 6         //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 = 2147483639
 7         if (newCapacity - MAX_ARRAY_SIZE > 0)//当得到的新数组长度比 MAX_ARRAY_SIZE 大时,调用 hugeCapacity 处理大数组
 8             newCapacity = hugeCapacity(minCapacity);
 9         //调用 Arrays.copyOf 将原数组拷贝到一个大小为newCapacity的新数组(注意是拷贝引用)
10         elementData = Arrays.copyOf(elementData, newCapacity);
11     }
12     
13     private static int hugeCapacity(int minCapacity) {
14         if (minCapacity < 0) // 
15             throw new OutOfMemoryError();
16         return (minCapacity > MAX_ARRAY_SIZE) ? //minCapacity > MAX_ARRAY_SIZE,则新数组大小为Integer.MAX_VALUE
17             Integer.MAX_VALUE :
18             MAX_ARRAY_SIZE;
19     }

对于 ArrayList 集合添加元素,我们总结一下:
  ①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
  ②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
  ③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
  ④、第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(这个数是要进行扩容) 次添加元素,为了防止溢出,此时会直接创建一个 1431655759+1 大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。
  ⑤、第 Integer.MAX_VALUE - 7 次添加元素时,创建一个大小为 Integer.MAX_VALUE 的数组,在进行元素添加。
  ⑥、第 Integer.MAX_VALUE + 1 次添加元素时,抛出 OutOfMemoryError 异常。
  注意:能向集合中添加 null 的,因为数组可以有 null 值存在。
  删除元素:

  1. 根据给定索引删除指定元素:
    /**
   * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);//判断给定索引的范围,超过集合大小则抛出异常

        modCount++;
        E oldValue = elementData(index);//得到索引处的删除元素

        int numMoved = size - index - 1;
        if (numMoved > 0) //size-index-1 > 0 表示 0<= index < (size-1),即索引不是最后一个元素
         //通过 System.arraycopy()将数组elementData 的下标index+1之后长度为 numMoved的元素拷贝到从index开始的位置
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work  将数组最后一个元素置为 null,便于垃圾回收
 
        return oldValue; // 将被删除的元素返回
    }

2.根据元素值删除:

    /**
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {  //用if判断了一次 就直接返回,所以remove是删除第一次出现的元素
                    fastRemove(index);  
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

修改元素:
修改元素就比较简单,先调用rangeCheck判断给定索引是否超出范围,再替换索引出元素,返回被替换的值。

   /**
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index);

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

获得元素:
根据索引获得元素。
在这里插入图片描述
根据元素获得索引,还有 lastIndexOf(Object o) 方法是返回最后一次出现该元素的下标。

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null) //依然是用的if判断就直接返回,所以也是获得第一次出现该元素的索引
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;   //如果没有,就返回-1;
    }

猜你喜欢

转载自blog.csdn.net/weixin_42255265/article/details/83383236