java基础知识之ArrayList源码探究(一)

ArrayList基本原理

底层是基于一个Object[]数组来维护数据

  /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    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.
     */
    transient Object[] elementData; // non-private to simplify nested class access

ArrayList优缺点

缺点:
容量受限时,需要进行数据扩容,进行元素拷贝会影响性能
2.频繁删除和往中间插入元素时,产生元素挪动,也会进行元素拷贝
3.不是线程安全的。
优点:
随机访问某个元素,性能很好;

ArrayList扩容原理

1.ArrayList list = new ArrayList();
这个动作做了什么

/**
     * Constructs an empty list with an initial capacity of ten.
     */
public ArrayList() {
   this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

可以看出,它将elementData数组的地址指向了DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址,

ArrayList Object[] elementData Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA int size JVM 空数组

2.list.add(“Hello ArrayList”)这个动作探究;

/**
     * Appends the specified element to the end of this list.
     * 将一个给定的元素添加到列表的末尾
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

方法ensureCapacityInternal确保内部容量的意思,

第一次添加元素,确保内部容量大小
放入数组
int DEFAULT_CAPACITY = 10
int size 默认值0
list.add()
ensureCapacityInternal() 实参:size+1=1
elementData[size++] = e

继续看ensureCapacityInternal()

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

先来看看calculateCapacity()方法

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

其中,minCapacity 值为1,DEFAULT_CAPACITY值为10

/**
     * Default initial capacity.
     * 默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;

所以calculateCapacity方法返回值为10,即ensureExplicitCapacity(10);

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

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

显然minCapacity - elementData.length = 10 大于0,满足if条件,于是执行grow()方法,此方法就是ArrayList扩容原理的核心方法,

/**
     * 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;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            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);
    }

很明显,oldCapacity的值为elementData数组的长度为0,newCapacity的值为1.5倍的oldCapacity,所以newCapacity值也为0,而minCapacity为10,所以第一个if条件成立,于是newCapacity = 10;
最后还执行elementData = Arrays.copyOf(elementData, newCapacity); 这是grow方法的另一个核心步骤,数组的拷贝;从脉络上看,这里的Array.copyOf它实参是elementData和newCapacity=10。可以看到他的内部直接调用了一个重载方法,在重载方法里,首先是一个三元表达式和一个System.arraycopy方法调用。

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    
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(original, 0, copy,0,Math.min(original.length, newLength));这句代码应该是操作了original和copy的样子。
仔细分析,elementData的class是Object[].class的类型,可以看出三元表达式会执行(T[]) new Object[newLength]。接着就会执行System.arraycopy这个方法,你可以查阅下JDK的API,它的主要作用是数组的拷贝。原来ArrayList的缺点原因在这里。

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

如果难以理解这个方法的代码,可以举个例子:
假设有两个数组src和dest,他们都有5个元素0,1,2,3,4,即src[0,1,2,3,4] dest[0,1,2,3,4]
问题:执行System.arraycopy(src, 3, dest, 1, 2)后是什么意思?
答案:就是从src源数组3位置开始,移动两个元素到dest数组1开始覆盖,结果就是dest变成[0,3,4,3,4]
所以System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));就很号理解,就是从original位置0开始移动Math.min(original.length, newLength)(此时为0)个元素到copy数组,从copy数组0位置开始覆盖,等于什么也没有拷贝,直接返回了新创建的数组T[] copy ,这个数组的长度为10。
回到最开始,内部容量确定后,直接执行elelmentData[size++] = e;通过数组的赋值操作,就完成了第一次元素的添加了。
可以发现,当添加开始的时候,如果大小不够就会进入grow方法,会进行一次1.5倍的扩容,所以代码规范一般建议我们要制定ArrayList的大小,如果不指定,可能会造成频繁的扩容和拷贝,导致性能低下
总结:
1.如果创建ArrayList不指定大小,ArrayList第一次添加元素时,会指定默认的容量大小为10。
2.ArrayList的扩容机制,通过grow方法,它的核心是一个计算扩容大小,是基于右移1位来计算,扩容大小为1.5倍,另一个核心是数组拷贝,底层通过System.arraycopy来创建新数组,来最终实现空间扩容。
最后,完善前面的流程图:

第一次添加元素
1.确保内部容量大小
返回
2.确保精确容量
相等
不相等
int DEFAULT_CAPACITY = 10
int size 默认值0
list.add()
calculateCapacity()
形参minCapacity = 1
ensureCapacityInternal()
实参:size+1=1
elementData[size++] = e
ensureExplicitCapacity()
形参minCapacity=10
elementData ==
DEFAULTCAPACITY_
EMPTY_ELEMENTDATA
返回
DEFAULT_CAPACITY=10
返回
minCapacity
minCapacity -
elementData.length>0
grow()
形参minCapacity=10
eusureExplicitCapacity
什么都不做,方法返回
newCapacity = oldCapacity+
(oldCapacity>>1)
在原有大小上,扩容1.5倍
newCapacity-minCapacity<0?
newCapacity=minCapacity,
newCapacity值为10
newCapacity-MAX_ARRAY_SIZE>0?
hugeCapacity()方法
数组拷贝操作
elementData=
Arrays.copyOf(elementData,newCapacity);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值