privatevoidlinkFirst(E e){ //将内部保存的首节点点赋值给f final Node<E> f = first; //创建新节点,新节点的next节点是当前的首节点 final Node<E> newNode = new Node<>(null, e, f); //把新节点作为新的首节点 first = newNode; //判断是否是第一个添加的元素 //如果是将新节点赋值给last //如果不是把原首节点的prev设置为新节点 if (f == null) last = newNode; else f.prev = newNode; //更新链表节点个数 size++; //将集合修改次数加1 modCount++; }
voidlinkLast(E e){ //将内部保存的尾节点赋值给l final Node<E> l = last; //创建新节点,新节点的prev节点是当前的尾节点 final Node<E> newNode = new Node<>(l, e, null); //把新节点作为新的尾节点 last = newNode; //判断是否是第一个添加的元素 //如果是将新节点赋值给first //如果不是把原首节点的next设置为新节点 if (l == null) first = newNode; else l.next = newNode; //更新链表节点个数 size++; //将集合修改次数加1 modCount++; }
publicbooleanremove(Object o){ //因为LinkedList允许存在null,所以需要进行null判断 if (o == null) { //从首节点开始遍历 for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { //调用unlink方法删除指定节点 unlink(x); returntrue; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); returntrue; } } } returnfalse; }
//删除指定节点 E unlink(Node<E> x){ //获取x节点的元素,以及它上一个节点,和下一个节点 final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; //如果x的上一个节点为null,说明是首节点,将x的下一个节点设置为新的首节点 //否则将x的上一节点设置为next,将x的上一节点设为null if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } //如果x的下一节点为null,说明是尾节点,将x的上一节点设置新的尾节点 //否则将x的上一节点设置x的上一节点,将x的下一节点设为null if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } //将x节点的元素值设为null,等待垃圾收集器收集 x.item = null; //链表节点个数减1 size--; //将集合修改次数加1 modCount++; //返回删除节点的元素值 return element; }
1 2 3 4 5 6
//删除指定位置的节点,其实和上面的方法差不多 //通过node方法获得指定位置的节点,再通过unlink方法删除 public E remove(int index){ checkElementIndex(index); return unlink(node(index)); }
1 2 3 4
//删除首节点 public E remove(){ return removeFirst(); }
1 2 3 4 5 6 7 8
//删除首节点 public E removeFirst(){ final Node<E> f = first; //如果首节点为null,说明是空链表,抛出异常 if (f == null) thrownew NoSuchElementException(); return unlinkFirst(f); }
//删除首节点 private E unlinkFirst(Node<E> f){ //首节点的元素值 final E element = f.item; //首节点的下一节点 final Node<E> next = f.next; //将首节点的元素值和下一节点设为null,等待垃圾收集器收集 f.item = null; f.next = null; // help GC //将next设置为新的首节点 first = next; //如果next为null,说明说明链表中只有一个节点,把last也设为null //否则把next的上一节点设为null if (next == null) last = null; else next.prev = null; //链表节点个数减1 size--; //将集合修改次数加1 modCount++; //返回删除节点的元素值 return element; }
1 2 3 4 5 6 7 8
//删除尾节点 public E removeLast(){ final Node<E> l = last; //如果首节点为null,说明是空链表,抛出异常 if (l == null) thrownew NoSuchElementException(); return unlinkLast(l); }
private E unlinkLast(Node<E> l){ //尾节点的元素值 final E element = l.item; //尾节点的上一节点 final Node<E> prev = l.prev; //将尾节点的元素值和上一节点设为null,等待垃圾收集器收集 l.item = null; l.prev = null; // help GC //将prev设置新的尾节点 last = prev; //如果prev为null,说明说明链表中只有一个节点,把first也设为null //否则把prev的下一节点设为null if (prev == null) first = null; else prev.next = null; //链表节点个数减1 size--; //将集合修改次数加1 modCount++; //返回删除节点的元素值 return element; }
删除和添加一样,其实本质只有三种方式,即删除首部、尾部、中间节点。如下图所示:
和添加一样,首尾删除很高效,删除中间元素比较低效要先找到节点位置,再修改前后指针。
获取元素
LinkedList提供了get、getFirst、getLast等方法获取节点元素值。
1 2 3 4 5 6 7
//获取指定位置的元素值 public E get(int index){ //判断是否越界 checkElementIndex(index); //直接调用node方法获取指定位置的节点,并反回其元素值 return node(index).item; }
1 2 3 4 5 6 7 8
//获取链表首节点的元素值 public E getFirst(){ final Node<E> f = first; //判断是否是空链表,如果是抛出异常,否则直接返回首节点的元素值 if (f == null) thrownew NoSuchElementException(); return f.item; }
1 2 3 4 5 6 7 8
//获取链表尾节点的元素值 public E getLast(){ final Node<E> l = last; //判断是否是空链表,如果是抛出异常,否则直接返回尾节点的元素值 if (l == null) thrownew NoSuchElementException(); return l.item; }
更新指定位置节点的元素值
1 2 3 4 5 6 7 8 9 10 11
public E set(int index, E element){ //判断是否越界 checkElementIndex(index); //指定位置的节点 Node<E> x = node(index); E oldVal = x.item; //设置新值 x.item = element; //返回老值 return oldVal; }
关于队列的操作
LinkedList可以作为FIFO(First In First Out)的队列,也就是先进先出的队列使用,以下是关于队列的操作。
//获取队列的第一个元素,如果为null会返回null public E peek(){ final Node<E> f = first; return (f == null) ? null : f.item; } //获取队列的第一个元素,如果为null会抛出异常 public E element(){ return getFirst(); } //获取队列的第一个元素,如果为null会返回null public E poll(){ final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //获取队列的第一个元素,如果为null会抛出异常 public E remove(){ return removeFirst(); } //将元素添加到队列尾部 publicbooleanoffer(E e){ return add(e); }
关于双端队列的操作
双端列队可以作为栈使用,栈的特性是LIFO(Last In First Out),也就是后进先出。所以作为栈使用也很简单,添加和删除元素都只操作队列的首节点即可。
//将元素添加到首部 publicbooleanofferFirst(E e){ addFirst(e); returntrue; } //将元素添加到尾部 publicbooleanofferLast(E e){ addLast(e); returntrue; } //获取首部的元素值 public E peekFirst(){ final Node<E> f = first; return (f == null) ? null : f.item; } //获取尾部的元素值 public E peekLast(){ final Node<E> l = last; return (l == null) ? null : l.item; } //删除首部,如果为null会返回null public E pollFirst(){ final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //删除尾部,如果为null会返回null public E pollLast(){ final Node<E> l = last; return (l == null) ? null : unlinkLast(l); } //将元素添加到首部 publicvoidpush(E e){ addFirst(e); } //删除首部,如果为null会抛出异常 public E pop(){ return removeFirst(); } //删除链表中元素值等于o的第一个节点,其实和remove方法是一样的,因为内部还是调用的remove方法 publicbooleanremoveFirstOccurrence(Object o){ return remove(o); }
//删除链表中元素值等于o的最后一个节点 publicbooleanremoveLastOccurrence(Object o){ //因为LinkedList允许存在null,所以需要进行null判断 if (o == null) { //和remove方法的区别它是从尾节点往前遍历 for (Node<E> x = last; x != null; x = x.prev) { if (x.item == null) { //调用unlink方法删除指定节点 unlink(x); returntrue; } } } else { for (Node<E> x = last; x != null; x = x.prev) { if (o.equals(x.item)) { unlink(x); returntrue; } } } returnfalse; }
//从前往后查找返回节点元素值等于o的位置,不存在返回-1 publicintindexOf(Object o){ int index = 0; 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; }
//该方法和上面indexOf方法相反,它是从后往前查找返回节点元素值等于o的位置,不存在返回-1 publicintlastIndexOf(Object o){ int index = size; if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { index--; if (x.item == null) return index; } } else { for (Node<E> x = last; x != null; x = x.prev) { index--; if (o.equals(x.item)) return index; } } return -1; }
//克隆函数,返回LinkedList的克隆对象 public Object clone(){ LinkedList<E> clone = superClone();
// 将链表中所有节点的数据都添加到克隆对象中 for (Node<E> x = first; x != null; x = x.next) clone.add(x.item);
return clone; }
//返回LinkedList节点单元素值的Object数组 public Object[] toArray() { Object[] result = new Object[size]; int i = 0; //将链表中所有节点的元素值添加到object数组中 for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
// 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型 public <T> T[] toArray(T[] a) { //如果a的长度小于LinkedList节点个数,说明a不能容纳LinkedList的所有节点元素值 //则新建一个数组,数组大小为LinkedList节点个数,并赋值给a if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; // 将链表中所有节点的元素值都添加到数组a中 for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item;
if (a.length > size) a[size] = null;
return a; }
//将LinkedList中的数据写入到输入流中,先写容量,再写数据 privatevoidwriteObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject();
// Write out size s.writeInt(size);
// Write out all elements in the proper order. for (Node<E> x = first; x != null; x = x.next) s.writeObject(x.item); }
//从输入流中读取数据,一样是先读容量,再读数据 privatevoidreadObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject();
// Read in size int size = s.readInt(); //从输入流中将元素值逐个添加到链表中 // Read in all elements in the proper order. for (int i = 0; i < size; i++) linkLast((E)s.readObject()); }