远古时期-数据库连接查询处理
第一次调试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时就是每次添加一个,扩容一次,扩容会复制新的数组,对性能影响有点大