/** * 删除该列表中指定位置的元素。 将任何后续元素移动到左侧(从其索引中减去一个元素)。 */ public E remove(int index) { rangeCheck(index);
modCount++; EoldValue= elementData(index);
intnumMoved= size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work //从列表中删除的元素 return oldValue; }
// Write out size as capacity for behavioural compatibility with clone() s.writeInt(size);
// Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } // 比对modCount,若变化则标识出现并发问题 if (modCount != expectedModCount) { thrownewConcurrentModificationException(); } }
modCount用来标记数组结构的改变。每一次数组发生结构改变(比如说增与删)这个变量都会自增。当 List 进行遍历的时候,遍历的前后会检查这个遍历是否被改变,如果有改变则将抛出异常。 这种设计体现了 fail-fast 思想,这是一种编程哲学。尽可能的抛出异常而不是武断的处理一个可能会造成问题的异常。这种思想在很多应用场景的开发(尤其是多线程高并发)都有着普遍的应用。
// 将元素插入链表尾 publicbooleanadd(E e) { linkLast(e); returntrue; } /** * 链接使e作为最后一个元素。 */ voidlinkLast(E e) { // 记录原末尾元素 final Node<E> l = last; // 新建节点 final Node<E> newNode = newNode<>(l, e, null); // 移动last指针 last = newNode; // 若空链表 则将头指向该节点 if (l == null) first = newNode; else // 否则将尾节点延长 l.next = newNode;//指向后继元素也就是指向下一个元素 size++; modCount++; }
// 指定位置插入元素 publicvoidadd(int index, E element) { checkPositionIndex(index); //检查索引是否处于[0-size]之间
if (index == size)//添加在链表尾部 linkLast(element); else//添加在链表中间 linkBefore(element, node(index)); } voidlinkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = newNode<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
publicbooleanaddAll(Collection<? extends E> c) { return addAll(size, c); } // 指定位置插入集合 publicbooleanaddAll(int index, Collection<? extends E> c) { //1:检查index范围是否在size之内 checkPositionIndex(index);
//2:toArray()方法把集合的数据存到对象数组中 Object[] a = c.toArray(); intnumNew= a.length; if (numNew == 0) returnfalse;
//3:得到插入位置的前驱节点和后继节点 Node<E> pred, succ; //如果插入位置为尾部,前驱节点为last,后继节点为null if (index == size) { succ = null; pred = last; } //否则,调用node()方法得到后继节点,再得到前驱节点 else { succ = node(index); pred = succ.prev; }
// 4:遍历数据将数据插入 for (Object o : a) { @SuppressWarnings("unchecked")Ee= (E) o; //创建新节点 Node<E> newNode = newNode<>(pred, e, null); //如果插入位置在链表头部 if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; }
//如果插入位置在尾部,重置last节点 if (succ == null) { last = pred; } //否则,将插入的链表与先前链表连接起来 else { pred.next = succ; succ.prev = pred; }
public E get(int index) { checkElementIndex(index); return node(index).item; }
// 获取头结点的方法 区别在于头结点为null时,抛出异常还是null public E getFirst() { final Node<E> f = first; if (f == null) thrownewNoSuchElementException(); return f.item; } public E element() { return getFirst(); } public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; }
public E peekFirst() { final Node<E> f = first; return (f == null) ? null : f.item; }
publicintindexOf(Object o) { intindex=0; // 判断是否是null 来避免npe问题 if (o == null) { //从头遍历 for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { //从头遍历 for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; }
Node方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 获取指定序号的Node Node<E> node(int index) { // 前一半 正序查 后一半 倒序查 if (index < (size >> 1)) { Node<E> x = first; for (inti=0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (inti= size - 1; i > index; i--) x = x.prev; return x; } }