LinkedList的数据结构就是双向链表,如下所示:
private static class Node<E> { E item;//数据元素 Node<E> next;//后继节点 Node<E> prev;//前驱节点 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
构造器:
transient int size = 0;//数据个数 transient Node<E> first;//表示链表的第一个节点 transient Node<E> last;//表示链表的最后一个节点 public LinkedList() { } public LinkedList(Collection<? extends E> c) {//用于整合Collection类型的数据 this(); addAll(c); }
add:
public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) {//采用的是尾插法 final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null);//新节点的前驱指向last的地址,后继为null,
//所以说这是一个双向链表,但不是循环的,循环的话,后继指向头节点 last = newNode;//让last指向新节点,也就说这个新节点是链表的最后一个元素
if (l == null)//当第一次添加时,first,last都是null,如果last是null,表明这是一个空链表 first = newNode;//就让新节点指向first,现在first和last都是同一个节点 else l.next = newNode;//当在添加数据时,就让老链表的最后一个节点的后继指向新节点(那个节点本来是null的)
size++; //长度加1 modCount++;
/**
总结:
新建一个节点,让新节点的前驱指向老链表的最后一个节点
让老链表的最后一个节点的后继指向新节点
让新节点变成链表的最后一个节点
长度加1
第一个节点前驱为null,最后一个节点后继为null
*/ }
get:
public E get(int index) { checkElementIndex(index);//检查一下索引是否在0到size的范围内 return node(index).item; } Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) {//看看索引的位置是在链表的前半部分还是后半部分,决定正着搜索或倒着搜索,找到后返回就行啦 Node<E> x = first; for (int i = 0; i < index; i++)//在这里看到链表是从0开始的 x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
remove:
public E remove(int index) { checkElementIndex(index);//先检查一下索引 return unlink(node(index)); } //先拿着索引找到这个节点 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; } x.item = null; size--; modCount++; return element; }