privatevoidlinkFirst(E e){ final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
first,首节点会一直被记录,这样就非常方便头插。
插入时候会创建新的节点元素, new Node<>(null, e, f),紧接着把新的头元素赋值给first。
@Test publicvoidtest_ArrayList_addFirst(){ ArrayList<Integer> list = new ArrayList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { list.add(0, i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
@Test publicvoidtest_LinkedList_addFirst(){ LinkedList<Integer> list = new LinkedList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { list.addFirst(i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
voidlinkLast(E e){ final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
与头插代码相比几乎没有什么区别,只是first换成last
耗时点只是在创建节点上, Node<E>
2.2.2 验证
「ArrayList、LinkeList,尾插源码验证」
@Test publicvoidtest_ArrayList_addLast(){ ArrayList<Integer> list = new ArrayList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { list.add(i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
@Test publicvoidtest_LinkedList_addLast(){ LinkedList<Integer> list = new LinkedList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { list.addLast(i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
publicvoidadd(int index, E element){ checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }
「位置定位node(index):」
Node<E> node(int index){ // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
size >> 1,这部分的代码判断元素位置在左半区间,还是右半区间,在进行循环查找。
「执行插入:」
voidlinkBefore(E e, Node<E> succ){ // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
找到指定位置插入的过程就比较简单了,与头插、尾插,相差不大。
整个过程可以看到,插入中比较耗时的点会在遍历寻找插入位置上。
2.3.2 验证
「ArrayList、LinkeList,中间插入源码验证」
@Test publicvoidtest_ArrayList_addCenter(){ ArrayList<Integer> list = new ArrayList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { list.add(list.size() >> 1, i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
@Test publicvoidtest_LinkedList_addCenter(){ LinkedList<Integer> list = new LinkedList<Integer>(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { list.add(list.size() >> 1, i); } System.out.println("耗时:" + (System.currentTimeMillis() - startTime)); }
publicbooleanremove(Object o){ if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); returntrue; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); returntrue; } } } returnfalse; }
这一部分是元素定位,和 unlink(x)解链。循环查找对应的元素,这部分没有什么难点。
「unlink(x)解链」
E unlink(Node<E> x){ // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev;
if (prev == null) { first = next; } else { prev.next = next; x.prev = null; }
if (next == null) { last = prev; } else { next.prev = prev; x.next = null; }