一次次的调试 集合类ArrayList的日记

第一次调试arrayList JAVA8

部分源码

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 默认容量大小10
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例。是一个static类型的final 空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,以了解添加第一个元素时要达到多少。
     */
    private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
存储元素的数组缓冲区。
容量是这个阵列缓冲区的长度。
当第一个元素被添加时,
任何空的值都将被扩展到DEFAULT_CONTANY。
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *ArrayList的元素个数
     * @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
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 构造初始容量为10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

第一类默认构造函数。以及添加扩容

测试代码
ArrayList arrayList = new ArrayList();使用shift+F7智能步入
这里的处理相比以前版本的处理有一些不同 ,这里因为是默认容量的大小,所以会先将一个空的Object数组赋值给该对象的elementData来实现。所以我们说ArrayList是根据数组来实现的。

添加
arrayList.add(1,2);

add

  /**
         *在此列表中指定位置的指定元素。将当前位于该位置的元素(如果有的话)和任何后续元素移到右边(将一个元素添加到它们的索引中)。
         * @param index index at which the specified element is to be inserted
         * @param element element to be inserted
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        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++;//列表大小+1
        }

rangeCheckForAdd

 /**
     * A version of rangeCheck used by add and addAll.
     * 添加范围检查的一个版本
     * 如果插入位置大于现在目前的大小和要报错
     * 如果你使用空参构造函数,那么在这里检查index是否会大于现在arraylist的元素个数,或者说这个要求的元素位置不合法
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

ensureCapacityInternal

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //这里说明构造器是无参构造器产生的实例,默认容量10,如果这里的元素要比10和你即将插入元素后的元素个数哪个会多的,取两个数的最大值作为最小容量 。这里调用时这里传入的参数为size+1
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

ensureExplicitCapacity


private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
// 此列表已被修改的次数。
        // 防溢出代码
        if (minCapacity - elementData.length > 0)
        //如果最小容量值大于目前数组的长度。如果我们刚初始化的时候(无参),调用grow
            grow(minCapacity);
    }

grow

增长的代码

 /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *增加容量,以确保它至少可以容纳最小容量参数指定的元素。
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//旧的容量目前是0
        int newCapacity = oldCapacity + (oldCapacity >> 1);新的容量会是旧容量的3倍
        
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//如果新的容量<最小容量参数,更新新值
        if (newCapacity - MAX_ARRAY_SIZE > 0) //如果新容量都大于了最大容许大小,大概最大整数-8
            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) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

数组拷贝使用了数组工具类:Arrays

  // Cloning

/**
 复制指定的数组,或者(如果需要的话)再拷贝(如果需要),使副本具有指定的长度。对于在原始数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于副本中有效的任何索引(而不是原件),
 * @param <T> the class of the objects in the array
 * @param original the array to be copied
 * @param newLength the length of the copy to be returned
 * @return a copy of the original array, truncated or padded with nulls
 *     to obtain the specified length
 * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
 * @throws NullPointerException if <tt>original</tt> is null
 * @since 1.6
 */
 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

copyof方法

 /**
//这里是第一次添加数据,也就是第一次扩容10,但是这里没有复制数据,因为现在列表是空的
     */
    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;
    }
    //调用了System.arraycopy()方法

也就是说grow将会返回一个扩容后的数组。
然后回到add的arrayCopy代码,

经过一次add操作后,当我们再添加时计算容量,返回的minCapacity是2,因为不会走那个判断是否是新创建的list的判断了,
但是modCount还是要+,完了之后还需要有的

 System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);//当插入数据是某个特定索引,需要右移所有它右边的数据 ,如果正好是末尾,则事实上size = index;
        elementData[index] = element;//赋值

这种构造方式当你填写到索引为10 ,即第11个数的时候就会使得minCapital=11;此时会走到防溢出的代码,也就是代码grow()了,
此时旧的容量是10,新的容量等于1.5倍旧的容量,然后旧再次调用copy将之前的数据全部拷贝进新的数组里边。
在这里插入图片描述
增长之前是10,最小容量等于当前size+1 ,新的容量是10+10>>1 就是15
这种方式的以后再补充其他操作,这里只有添加和扩容
Arraylist不支持跳过index,只能顺序添加或者改变。
例如0 ,2的添加,必须要0,1,2添加。

指定容量的方式

初始化容量>0

测试代码

        /*ArrayList arrayList2 = new ArrayList(9);
        arrayList2.add(0,0);
        arrayList2.add(1,1);
        for (int i = 0; i < 6 ; i++) {
            arrayList2.add(i+2,1);
        }
        arrayList2.add(7,7);
        arrayList2.add(8,8);
        arrayList2.add(9,9);*/

源码

 /**
     * 构造具有指定初始容量的空列表。
     *
     * @param list的初始容量
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
//如果容量>0 则构造一个指定定容量的数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
            //如果参数是0 ,则将事先有的空数组赋值给该数组
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

添加

//指定参数的添加
 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++;
    }

minCapacity一般都值得的是你添加元素需要的大小
判断是否grow的唯一标识判断minCapacity>elementData.length
所以这个如果初始化很大的数组,一般不会太早grow();
此时到了需要扩容时
在这里插入图片描述
这里初始化大小9,9+9/2 = 13

初始化大小如果是0

测试代码

此时new的时候

  this.elementData = EMPTY_ELEMENTDATA;

此时第一次调用grow
在这里插入图片描述
这时因为旧的容量是0,所以新容量会是之前的minCapacity,然后就是复制拷贝数组了,所以这时再次添加就会再扩展一次,所以当你初始化容量为0时就是每次添加一个,扩容一次,扩容会复制新的数组,对性能影响有点大

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值