基本参数:
-
static final int DEFAULT_CAPACITY = 10;
- 默认初始容量为10
-
static final Object[] EMPTY_ELEMENTDATA = {};
- 可以存放任何类型元素的空数组
-
transient Object[] elementData;
- 对象数组,ArrayList的底层数据结构为数组
-
private int size;
- elementData中已经存放的元素的个数,注意:不是elementData的容量
ArrayList的扩容:
-
private Object[] grow() { return grow(size + 1); //传进数组元素个数+1的数量 }
-
private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; //假如数组长度不为空,使用ArraySupport.newLength方法扩容为原来的1.5倍 if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */); //数组长度右移1位,也就是除以2 return elementData = Arrays.copyOf(elementData, newCapacity); //将元素复制到原数组长度1.5倍的新数组 } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; //假如原数组长度为0,则初始化新的长度为10的数组 } } ===================================================================================== //ArraySupport中的newLength方法: public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 //oldLength为原数组长度,minGrowth为size+1减去原数组长度,preGrowth为原数组长度除以2 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; //比较minGrowth与preGrowth的大小,这里得到preGrowth较大,用preGrowth加上原数组长度,也就是用原数组长度×1.5 if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); }
构造方法:
-
有参构造:构造具有指定初始容量(即参数)的空列表
- 如果可以预先知道数组大小,则使用有参构造指定数组大小,可以避免数组扩容提升性能
- 如果初始容量为0时,数组为空,在进行添加元素时才会进行扩容创建需要的数组
public ArrayList(int initialCapacity) { //initialCapacity为自定义初始容量 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; //初始值大于0,则创建该容量大小的数组elementData } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; //初始值等于0,创建空的数组elementData } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } //初始值小于0,报错 }
-
空参构造:构造一个初始容量为10的空列表
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
ArrayList考虑到节省内存,仅仅是创建了ArrayList对象,初始化了一个空数组,在首次添加元素时,才真正初始化容量为10的数组
在空参构造中,使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA;空数组,而不是EMPTY_ELEMENTDATA,因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA首次扩容为10,而EMPTY_ELEMENTDATA是按照1.5倍扩容从0开始而不是10
其他方法:
-
查询指定元素出现的的索引:
int indexOfRange(Object o, int start, int end) { Object[] es = elementData; //将对象数组赋值给es数组 if (o == null) { for (int i = start; i < end; i++) { if (es[i] == null) { return i; } } //如果传进的元素为null,经过遍历数组返回Null的索引 } else { for (int i = start; i < end; i++) { if (o.equals(es[i])) { return i; } } //如果传进的元素不为null,遍历数组找到该元素返回索引 } return -1; } ============================================================================================ public int indexOf(Object o) { return indexOfRange(o, 0, size); } //计算索引的方法调用indexOfRange()方法,传进要查询的元素,并且从0开始,最多到数组中包含的元素个数
-
add()方法:
public boolean add(E e) { modCount++; //增加修改次数 add(e, elementData, size); //添加元素 return true; } private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); //如果容量不够进行扩容 elementData[s] = e; //数组扩容后,将元素个数作为下标,s-1是最后一个元素的索引,那么s就是要添加的元素所在的索引位置,所以将该位置赋值为e size = s + 1; //元素个数+1 }
public void add(int index, E element) { rangeCheckForAdd(index); //检查Index是否越界 modCount++; //增加修改次数 final int s; Object[] elementData; if ((s = size) == (elementData = this.elementData).length) elementData = grow(); //如果数组大小不够进行扩容 System.arraycopy(elementData, index, elementData, index + 1, s - index); //从数组中的index位置开始,将index及index之后的元素复制进同一个数组中的index+1的位置,也就是从index开始,所有之后的元素都往后挪了一位,空出了index位置 elementData[index] = element; //将index赋值为新元素 size = s + 1; }
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); //将指定源数组中的数组从指定位置复制到目标数组的指定位置。
src :源数组、 srcPos:源数组中的起始位置、 dest:目标数组、 destPos:目标数组的起始位置、 length:要复制的数组元素的数目
-
addAll()方法
public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); //将集合转变为数组 modCount++; int numNew = a.length; if (numNew == 0) return false; Object[] elementData; final int s; if (numNew > (elementData = this.elementData).length - (s = size)) elementData = grow(s + numNew); //如果将要添加的新集合的元素数量大于原集合的空位置,则将原数组进行扩容。扩容的大小必须至少能装下新集合 System.arraycopy(a, 0, elementData, s, numNew); size = s + numNew; return true; }
- add()方法中,每次扩容时,调用grow()空参方法,每次扩容1.5倍
- 而addAll()方法每次扩容,调用的是grow(size+numNew)有参方法,假如要求扩容后的空间小于原数组的1.5倍时,还扩容1.5倍,如果大于,那就扩容到刚好装下新集合
- addAll方法就是add方法的批量版本,优势在于避免了一个一个元素添加时的多次库容,提高了效率
-
contains方法:
public boolean contains(Object o) { return indexOf(o) >= 0; } //计算要查询元素的索引,当索引值>=0时,返回true
-
集合转数组:toArray()方法:
public Object[] toArray() { return Arrays.copyOf(elementData, size); } //调用Arrays工具类的复制方法,传进对象数组复制为一个新数组,新数组的长度为原数组的元素个数size
-
get()方法:
public E get(int index) { Objects.checkIndex(index, size); //检查传入的参数index,是否在0到size范围内 return elementData(index); //返回对象数组中索引为index的元素 }
-
set()方法:
public E set(int index, E element) { Objects.checkIndex(index, size); //检查传入的参数index,是否在0到size范围内 E oldValue = elementData(index); //旧值为数组对象中索引为index的值 elementData[index] = element; //将传入的新值传入数组中index索引的位置 return oldValue; //返回旧值 }
-
remove()方法:
public E remove(int index) { Objects.checkIndex(index, size); //检测 index 是否越界 final Object[] es = elementData; //记录该索引的原值 @SuppressWarnings("unchecked") E oldValue = (E) es[index]; fastRemove(es, index); //快速移除 return oldValue; } ===================================================================================== private void fastRemove(Object[] es, int i) { modCount++; //增加修改次数 //如果i不是最末尾的元素,则将i+1位置的数组往前挪 final int newSize; if ((newSize = size - 1) > i) //size-1的原因是:size是从1开始数,而索引下标从0开始 System.arraycopy(es, i + 1, es, i, newSize - i); //将新的末尾元素变为null es[size = newSize] = null; }
-
public boolean remove(Object o) { final Object[] es = elementData; final int size = this.size; //找首个元素为O的位置 int i = 0; found: { if (o == null) { //如果o为Null for (; i < size; i++) //遍历数组 if (es[i] == null) break found; } else { for (; i < size; i++) if (o.equals(es[i])) break found; } return false; //如果没找到返回false } fastRemove(es, i); //快速移除 return true; //找到了返回true }
-
-
hashCode()方法:
public int hashCode() { //得到当前数组的修改次数 int expectedModCount = modCount; //计算哈希值 int hash = hashCodeRange(0, size); //如果修改次数发生变化,则抛出ConcurrentModificationException异常 checkForComodification(expectedModCount); return hash; } int hashCodeRange(int from, int to) { final Object[] es = elementData; //如果元素个数超过数组长度,抛出ConcurrentModificationException异常 if (to > es.length) { throw new ConcurrentModificationException(); } //遍历每一个元素 *31求哈希值 int hashCode = 1; for (int i = from; i < to; i++) { Object e = es[i]; hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); } return hashCode; }
-
选择*31的原因:
- 选的太小容易哈希冲突,太大的话容易导致Int类型的溢出
- 使用31可以使用移位运算进行简化,效率高:31*i==( i < < 5 ) - i
-
选择*31的原因:
-
clear()方法:
public void clear() { modCount++; final Object[] es = elementData; for (int to = size, i = size = 0; i < to; i++) es[i] = null; //先将数组元素个数清零,再将每一个索引位置赋值为null }
-
equals方法:
public boolean equals(Object o) { //如果元素就是本身,返回true相等 if (o == this) { return true; } //如果不为List类型 直接返回false不相等 if (!(o instanceof List)) { return false; } boolean equal = root.equalsRange((List<?>)o, offset, offset + size); checkForComodification(); //检测modCount是否被更改过,如果被更改贼抛出并发修改异常 return equal; } boolean equalsRange(List<?> other, int from, int to) { final Object[] es = elementData; if (to > es.length) { throw new ConcurrentModificationException(); //如果to 大于 es的大小,这说明发生了改变,抛出并发修改异常 } //通过迭代器遍历 other 然后逐个进行比对 var oit = other.iterator(); for (; from < to; from++) { //如果 oit没有下一个,或者元素不相等,返回false不匹配 if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) { return false; } } //通过检测oit是否遍历完成判断大小是否相等 return !oit.hasNext(); } //Objects的equals方法: public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }