ArrayList

瘦欲@ 提交于 2020-02-29 11:56:53

ArrayList

ArrayList以数组存储数据的集合。默认数组长度为10,是一个Object[] elementData的数组。

以构建不带构造参数的ArrayList来看看运行逻辑。

这里的默认就是一个空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

size是数组的大小默认为0。modCount表示数组新增,删除次数,默认为0,当使用方法为数组增加元素,删除元素时modCount会递增。

public boolean add(E e) {
  modCount++;//操作次数递增
  add(e, elementData, size);
  return true;
}

/**
注意:该方法不是静态方法,且是私有方法。
e : 要添加到数组的元素
elementData:被添加元素的数组
s:被添加元素的数组大小
*/
private void add(E e, Object[] elementData, int s) {
	//判断了传递的大小 == 数组的长度,这样不能避免空指针。
	//这样设计有个好处。此处elementData是持有对象数组的引用,s是对象数组大小,实际值副本引用,因此当数组被更改了,就不会往下执行,能规避一定的多线程下问题。
	//假设线程A,B同时操作该add方法,那么elementData与s都会是正确的。因此并不能完全的线程安全
	if (s == elementData.length)
		//grow:增长。增长并返回一个数组,再次修改elementData的引用
		//完成操作,此时的elementData = new Object[10];
		 elementData = grow();
	//将e添加到当前数组大小的位置。elementData[0] = e;
	elementData[s] = e;
	//由于size是从0开始,因此下个元素位置等于0+1 = 1。所以ArrayList的数组下标是从0开始往上+1
	size = s + 1;
	//注意:除了首个元素,其他元素为null。那么elementData的数组就只会有1个元素,该元素下标为0,elementData.length = 1,elementData[0] = object。不再是10个元素的数组
}

//数组增长1,并返回该数组的引用
private Object[] grow() {  
  return grow(size + 1);//当前数组大小+1
}

/**
minCapacity:最小容量
这里是上面方法的引用,首次为0+1 = 1,minCapacity = 1
*/
private Object[] grow(int minCapacity) {
	//获取当前数组的长度
	int oldCapacity = elementData.length;
	if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		/**
			若果当前数组内有元素了,或者不是默认数组就进来。
			假设当前数组大小为1
			oldCapacity = 1
			minCapacity - oldCapacity = 2 - 1 = 1
			oldCapacity >> 1 = 0。10的二进制=0b1, 右移 1位 = 0b0 = 0
			好,现在这个方法就是这样的了
			ArraysSupport.newLength(1,1,0);看Arrays.Support的代码块,其实就是判断了下大小,确定数组不会特别巨大。
			逻辑:newCapacity = ((1 >=0)?1 : 0 )+1。newCapacity 的大小就是 2
			
		*/
		int newCapacity =
			ArraysSupport.newLength(
					oldCapacity,
					minCapacity - oldCapacity,
					oldCapacity >> 1
					)
			];
		/**
		数组赋值,将elementData,赋值给一个长度为2的数组,并返回引用给elementData
		其中elementData的元素会被copy到新数组中,再返回该数组,看回add方法
		*/
		return elementData = Arrays.copyOf(elementData, newCapacity);
	} else {
		/**
			当前是数组长度为 <= 0 || 当前数组是默认的空数组就会进来。
			Math.max(DEFAULT_CAPACITY, minCapacity)这个max方法就是 a >= b ? a : b。
			DEFAULT_CAPACITY = 10。结合前面的传递过来的值,也就是10>1?10:1。因此会创建一个长度为10的Object数组并返回,看回add方法
		*/
		return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
	}
}

/**
get方法从指定的元素下标返回元素
*/
public E get(int index) {  
	//检查,如果超出长度就会抛出异常:IndexOutOfBoundsException
  Objects.checkIndex(index, size);  
 return elementData(index);  
}

E elementData(int index) { 
  //返回数组指定下标的元素,由于是通过下标访问元素,因此访问速度会很快,这样体现了数组的优势
  //添加元素新增指定数组,再复制元素到新数组,再返回新数组的引用给当前对象数组。因此新增效率不高,访问效率很高
  return (E) elementData[index];  
}

//根据数组中的某个元素
public E remove(int index) {
	//长度的检查
  Objects.checkIndex(index, size);
	
	//获取一个引用
 final Object[] es = elementData;

  @SuppressWarnings("unchecked")
  E oldValue = (E) es[index];//旧值,用于抛出

  fastRemove(es, index);

 return oldValue;
}

private void fastRemove(Object[] es, int i) {
	//操作数递增
  modCount++;
 final int newSize;
	//为了更直观,我添加了「{}」,在代码中{}是被省略了的
	//这里的逻辑来看一下,((newSize = size - 1) = 10-1=9)。9 > 0 == true
	if ((newSize = size - 1) > i){
		//数组复制,从es,从(0+1=)1开始,复制到es,从 0 开始,(9 - 1 =)8
		//复制数组,从1开始,复制8个元素,到当前数组的下标0的元素开始。完成删除
		System.arraycopy(es, i + 1, es, i, newSize - i);
	}
	//当前数组第9个为null。
	//假设i =9,且当前数组长度为10那么不走上面逻辑,直接设置为null,这是很快的
	es[size = newSize] = null;
}

ArraysSupport

/**
获取长度:
oldLength:1
minGrowth:1
prefGrowth:0
*/
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
	//newLength = ((1 >= 0)? 1 : 0 )+1。newLength = 2
	int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
	if (newLength - MAX_ARRAY_LENGTH <= 0) {
		return newLength;//抛出2
	}
	return hugeLength(oldLength, minGrowth);
}

Arrays

/**
原数组,新长度为
*/
public static <T> T[] copyOf(T[] original, int newLength) {
  return (T[]) copyOf(original, newLength, original.getClass());
}

/**
original原数组
newLength新长度,
newType原数组的类型
*/
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
	//只需要掌握一处,这里是一个new Object[2]
	T[] copy = ((Object)newType == (Object)Object[].class)
		?(T[]) new Object[newLength]
		:  (T[])Array.newInstance(newType.getComponentType(), newLength);
	
	//使用系统的复制,从原数组,0开始,到copy数组,从0开始,((1 <= 2)?1 : 2)=1
	//这里的复制也可以这么形容。把原数组从0开始复制到一个从0开始的新数组中,复制个数为1。
	System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
	
	//抛出copy数组
 return copy;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!