在JDK1.8中,ArrayList的起始化与扩容机制Increments modCount讲解

在ArrayList中,起始化方式有两种:
1.调用无参的构造方法:

public ArrayList() {
       //无参构造方法
			this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	//将ArrayList起始化为一个名叫DEFAULTCAPACITY_EMPTY_ELEMENTDATA的对象
	}

↓ 我们找到DEFAULTCAPACITY_EMPTY_ELEMENTDATA

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
     };
//	其实就是创建了一个名为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的Object[]
//	注意:为内容为{},可以理解为长度为0,但不是NUll;

2.调用有参的构造方法:

public ArrayList(int initialCapacity) {
     //有参构造方法
		if (initialCapacity > 0) {
     	//如果传入的参数大于0
			this.elementData = new Object[initialCapacity];
			//按照传入的参数起始化创建一个相应长度的Object[]
		} else if (initialCapacity == 0) {
     //如果传入的参数等于0
			this.elementData = EMPTY_ELEMENTDATA;
			//起始化创建一个长度为0的Object[],等同与无参构造
		} else {
     		//如果传入的参数等于0
			throw new IllegalArgumentException("Illegal Capacity: "+
											   initialCapacity);
			//抛一个名为非法参数异常
		}
	}

在明白了构造之后,我们来看看扩容。
首先扩容机制会在调用add方法添加元素时才会触发,我们阅读源码,会发现,扩容机制分为下列阶段:

  1. add(E e)
  2. ensureCapacityInternal(int minCapacity)
  3. ensureExplicitCapacity(int minCapacity)
  4. grow(int minCapacity)
    下面详细分析每个方法的作用:
    一:add方法
	// 添加元素
	public boolean add(E e) {
     
		
		ensureCapacityInternal(size + 1);
		// 按照元素个数+1,确认数组容量是否够用
		elementData[size++] = e;
		//将数组第size位置添加为该元素
		return true;
	}

看完add,我们明白了,要想搞明白是怎么进行扩容的,还得看看ensureCapacityInternal()的内容
二:ensureCapacityInternal方法

private void ensureCapacityInternal(int minCapacity) {
     
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
//这里是调用了ensureExplicitCapacity方法,而里面又调用calculateCapacity方法
	}

在这里不要急,我们先进入calculateCapacity()方法阅读一下

private static int calculateCapacity(Object[] elementData, int minCapacity) {
     
		if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     
		//判断数组是否起始化为{}
			return Math.max(DEFAULT_CAPACITY, minCapacity);
			//如果是,则返回10
		}
		return minCapacity;
		//返回数组需要的最小容量
	}

我们发现在alculateCapacity()方法中返回了一个容量值,其作为参数传入了ensureExplicitCapacity()之中,所以进入了下一步:
三: ensureExplicitCapacity方法

private void ensureExplicitCapacity(int minCapacity) {
     //判断当前容量是否够用
		modCount++;

		// overflow-conscious code
	if (minCapacity - elementData.length > 0)
	//判断当前容量是否够用
		grow(minCapacity);
		//如果不够,进去则进人grow
	}

在这里,我们终于来到了最关键的一步,也就是如果容量不够,则需要进入grow()方法进行扩容。
四: grow方法

private void grow(int minCapacity) {
     
		// overflow-conscious code
		int oldCapacity = elementData.length;
		//旧的容量为当前数组长度
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		//新的容量为当前数组长度的1.5倍
		if (newCapacity - minCapacity < 0)
		//判断当前的容量是否不足于需要的最小容量
			newCapacity = minCapacity;
			//如果不足,则新容量为当前需要的最小容量
		if (newCapacity - MAX_ARRAY_SIZE > 0)
		//判断现在的新容量是否大于int的最大容量-8
			newCapacity = hugeCapacity(minCapacity);
			//如果大于,则进去hugeCapacity方法返回新的容量
		elementData = Arrays.copyOf(elementData, newCapacity);
		//按照最新容量复制数组,实现扩容
	}

其中这一行代码为扩容的关键:
int newCapacity = oldCapacity + (oldCapacity >> 1);
oldCapacity >> 1表示做向右移一位运算,等同于取二分之一。
所以这一行代码的意义即为:去原数组长度再加上其的一半,总共1.5倍原数组长度为新长度。即数组容量按照1.5倍增长。
然后就是调用copyOf()方法,将旧数组的新容量个元素,copy到一个新数组中。
到这里扩容就完成了。
我们也可以顺带看一下hugeCapacity方法:

 private static int hugeCapacity(int minCapacity) {
     
		if (minCapacity < 0) 
		// 如果当前需要的最小容量小于0
			throw new OutOfMemoryError();
			//抛出容量超出的错误
		return (minCapacity > MAX_ARRAY_SIZE) ?
		//返回当前最小容量与MAX_ARRAY_SIZE谁大
			Integer.MAX_VALUE :
			//最小容量大,返回Integer.MAX_VALUE ,即2147483647
			MAX_ARRAY_SIZE;
			//否则返回MAX_ARRAY_SIZE,即2147483639
	}

在弄明白扩容原理后,我们再回过头看一下两种构造方式,都会如何实现集合元素的添加:
1.如果调用无参的构造方法:
例如

ArrayList<String> list = new ArrayList<String>();

则当第一次添加元素时:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
     
		if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     
		//这个时候满足了这个条件
			return Math.max(DEFAULT_CAPACITY, minCapacity);
		}
		return minCapacity;
	}

所以会返回DEFAULT_CAPACITY,即返回10.而10又作为参数,传入了ensureExplicitCapacity方法

private void ensureExplicitCapacity(int minCapacity) {
     
		modCount++;
	if (minCapacity - elementData.length > 0)
	//判断当前容量是否够用
		grow(minCapacity);
		//如果不够,进去则进人grow
	}

此时minCapacity为10,elementData.length为0,所以又会进入grow方法

private void grow(int minCapacity) {
     
		int oldCapacity = elementData.length;
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		if (newCapacity - minCapacity < 0)
		//判断当前的容量是否不足于需要的最小容量
			newCapacity = minCapacity;
			//如果不足,则新容量为当前需要的最小容量
		if (newCapacity - MAX_ARRAY_SIZE > 0)
			newCapacity = hugeCapacity(minCapacity);
		elementData = Arrays.copyOf(elementData, newCapacity);
	}

此时oldCapacity 就为0,newCapacity经过计算也为0,minCapacity还是为10,满足了newCapacity - minCapacity < 0条件,newCapacity = minCapacity,新的容量newCapacity就为10,
elementData = Arrays.copyOf(elementData, newCapacity),
所以扩容后的新数组长度就为10了,第一次扩容就结束了。
当需要传入第11个元素的时候,就会发生第二次扩容,按照我们上面讲解,第二次扩容即为10的1.5倍,即为15,后面的情况以此类推。
2.如果调用有参的构造方法:
例如

ArrayList<String> arrayList = new ArrayList<String>(100);

有了上面的讲解,这里就非常简单了:
当需要传入第101个元素的时候,就会发生扩容,按照我们上面讲解,扩容即为100的1.5倍,即为150,后面的情况以此类推

你可能感兴趣的:(JavaBase,java,集合)