ArrayList:
结构:数组
创建时:1.无参数初始化
2.指定大小初始化
3.指定初始数据初始化
扩容: 1.第一次add时,如果是空数组,将capacity初始化为10
2.add时超过当前容量,则扩容至当前容量的1.5倍
3.如果扩容1.5倍后不够,则直接扩容到指定的容量(原始容量+新增的元素数量)
4.如果所需容量大于Integer.MAX_VALUE - 8,则直接扩容到Integer.MAX_VALUE
扩容的本质 是调用System.arraycopy,是复制原数组到一个新的数组,因此非常消耗性能
删除:
1.删掉指定位置的元素:先拿到指定位置的元素的值,然后将后面所有元素整体前移一位,最后一位置null(方便gc),size--
2.删除指定元素时分两种情况,是null则找null,非null则找equeals的
新增: 允许add null
迭代器: 在用for进行循环时,是不能进行使用原list进行remove的,因为索引的值一直在变,每remove一个,后面的元素向前移一位.
因此如果要循环删除list中的元素,要使用Iterator
核心扩容代码
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后的值 < 我们的期望值,扩容后的值就等于我们的期望值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容后的值 > jvm 所能分配的数组的最大值,那么就用 Integer 的最大值
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 通过复制进行扩容,因此非常消耗性能
elementData = Arrays.copyOf(elementData, newCapacity);
}
LinkedList:
结构:双向链表结构
新增:只能从头部或尾部新增
删除:只能从头部或尾部删除
查询:根据索引来查询节点,查找时采用二分法
迭代器: 迭代器不同于其他迭代器,因为它是双向的,因此有它自身的ListIterator,可向前或向后迭代
迭代器的删除:,删除之前迭代器迭代到的值,无论之前是向前还是向后迭代,都在同一个方法中进行删除操作
迭代器初始化时,索引是0,也就是next指向第一个元素
最开始的时候只能next,不能previous
用previous的时候,返回的是上一次next指向的元素
如果使用next之前有previous的操作,那么这次next指向的是上一次previous返回的元素
核心代码{
迭代previous: lastReturn=next=(next==null?last:next.prev)//如果next为null,那么就是最后一个了,直接等于last
next : lastReturn=next;
next=next.next;
我觉得的linkedlist的重点
public void remove() {
checkForComodification();
// lastReturned 是本次迭代需要删除的值,分以下空和非空两种情况:
// lastReturned 为空,说明调用者没有主动执行过 next() 或者 previos(),直接报错
// lastReturned 不为空,是在上次执行 next() 或者 previos()方法时赋的值
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
//删除当前节点
unlink(lastReturned);
//因为在previous方法中,最后是将next=lastReturned的
if (next == lastReturned)
//如果之前执行的是previous,那么将删掉的next记录起来,下次直接取它的previous
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
ArrayList和LinkedList的主要区别有两点:
1.ArrayList的大小是受限制的,最大容量是Integer.MAX_VALUE,而LinkedList因为是双向联表结构,因此它在理论上是可以无限大的
2.迭代器不一样,linkedlist有它特殊的双向迭代器