List
框架
ArrayList
1. Fileds
int DEFAULT_CAPACITY; //初始容量,10
Object[] elementData; //底层数组
int size; //ArryList长度,元素个数
2. Contractors
- 无参构造
elementData指向空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA - 有参构造:传参初始容量
直接创建初始容量的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} //省略参数异常代码
}
- 有参构造:传参集合对象
本质是是用copyOf()方法创建新的数组
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
3. 扩容机制
结论:grow(int minCapacity)方法扩容。扩容时,newCapacity=oldCapacity+OldCapacty>>1
private Object[] grow(int minCapacity) {//minCapacity是所需的元素个数,其值=add前的数组的元素个数+需要add的元素个数
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
//第一次使用list时
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
ArraysSupport.newLength计算方式如下:
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// assert oldLength >= 0
// assert minGrowth > 0
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
if (newLength - MAX_ARRAY_LENGTH <= 0) {
return newLength;
}
return hugeLength(oldLength, minGrowth);
}
针对newCapacity=oldCapacity+OldCapacty>>1,而不是直接说newCapacity=oldCapacity*1.5,因为在初始化固定容量的list时可能不是直观的1.5倍。计算新容量的公式如下
newCapacity=Math.max(minCapacity - oldCapacity, oldCapacity >> 1) + oldLength
例如,初始化容量为1的list,第二次添加一个元素前,即newCapacity=Math.max(1,0)+1=2
第四次添加一个元素前,即newCapacity=Math.max(1,3)+1=4
综上,容量的变化为1 2 3 4 6…
扩容触发机制:size>elementData.length时。
add添加元素
- add()的策略:1.判断底层数组容量,够则直接添加元素,否则先扩容;2.添加。但添加操作需要注意的是 System.arraycopy方法的使用。
- add方法包括以下两类4种情况:增加单个元素(数组末尾追加),增加单个元素(在指定位置增加),增加集合(数组末尾追加),增加集合(在指定位置增加)。
a. add(E e)方法,方法内调用如下add方法,在数组尾追加。
b. add(int index,E e),在指定位置添加元素,原数组该位置之后的元素全部后移。private void add(E e, Object[] elementData, int s) { //1.判断底层数组容量 if (s == elementData.length) elementData = grow(); //2.添加。只有这里的数组添加操作时直接完成的,其他会调用System.arraycopy方法 elementData[s] = e; size = s + 1; } 该方法只会返回true。
c. addAll(Collection c)public void add(int index, E element) { //判断下标 rangeCheckForAdd(index); final int s; Object[] elementData; //1.判断底层数组容量 if ((s = size) == (elementData = this.elementData).length) elementData = grow(); //2.添加 System.arraycopy(elementData, index, elementData, index + 1, s - index); elementData[index] = element; size = s + 1; } 该方法无返回。
d. addAll(int index,Collection c),该方法与c中的方法基本一致public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Object[] elementData; final int s; //1.判断底层数组容量 if (numNew > (elementData = this.elementData).length - (s = size)) elementData = grow(s + numNew); //2.添加 System.arraycopy(a, 0, elementData, s, numNew); size = s + numNew; return true; } c中没有元素时返回false,其余返回true。
public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Object[] elementData; final int s; //1.判断底层数组容量 if (numNew > (elementData = this.elementData).length - (s = size)) elementData = grow(s + numNew); //2.添加 int numMoved = s - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size = s + numNew; return true; } c中没有元素时返回false,其余返回true。
remove删除元素
- ArrayLsit有两类3种remove方法。分别为remove(int index)、remove(E e)、removeAll(Collection c)。
a. 两种删除单个元素的remove方法,传参分别是下标、元素,两种元素本质上都调用了fastRemove(Object[] es, int i)方法。
b. 关于remove(int index)和remove(E e)方法,特别说明以下点:
①. 对于存放Integer类型元素的数组,删除方式建议:
②. 直接删除元素的方法,单次只能删除数组最先出现的元素。//删除list中下标为2的元素 list.remove(2); //删除list中值为2的元素 list.remove(Integer.valueOf(2));
③. 传参下标时,返回被删除的元素。传参元素时,返回boolean表示是否删除成功。
c. removeAll(Collection c)方法。内部调用了batchRemove方法。特别的,与add方法的返回值不同,如a.removeAll(b),表示删除a中的交集元素,但该方法的返回值表示集合a是否发生了变化,而非删除成功与否。
retainAll求交集
retainAll(Collection c)方法和removeAll一样,内部调用了batchRemove,返回值也是boolean。a.retainAll(b)表示求集合a和b的交集,并放在a中,返回值也表示集合a是否发生了变化。
方法的返回值表示集合a是否发生了变化,而非删除成功与否**。