LinkedList源码解析
2021/7/16 14:06:00
本文主要是介绍LinkedList源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
LinkedList
它是双向链表,实现了所有可选的列表操作,各种元素(除了 null)都可以作为结点
这里的索引一般都需要从头开始遍历,并且它是线程不安全的
看完这部分代码之后,对用 Java来写链表题肯定是非常熟悉了的
常见属性
// 链表的长度 transient int size = 0; // 指向首元结点的指针 transient Node<e> first; // 指向尾结点的指针 transient Node<e> last; // 序列化 ID private static final long serialVersionUID = 876323262645176354L;
构造方法
// 构造一个空链表 public LinkedList(){} // 构造一个链表,其中的元素来自于某一个指定的集合,元素排列顺序由指定集合的迭代顺序决定 public LinkedList(Collection<!--? extends E--> c) { this(); addAll(c); }
常见方法
E getFirst()
返回链表中的首元结点中存储的元素
public E getFirst() { // 直接通过实例变量获取首元结点 final Node<e> f = first; // 当该链表为空链表时,调用该方法会报错 if(f == null) { throw new NoSuchElementException(); } // 返回首元结点内的存储的元素信息 return f.item; }
E getLast()
返回列表中最后一个结点存储的元素信息
public E getLast() { final Node<e> l = last; if(l == null) { throw new NoSuchElementException(); } return l.item; }
E removeFirst
移除首元结点并返回其元素
public E removeFirst() { final Node<e> f = first; if(f == null) { throw new NoSuchELementException(); } // 方法定义在下面 return unlinkFirst(f); } private E unlinkFirst(Node<e> f) { // assert f == first && f != null; // 获取首元结点的元素,准备返回 final E element = f.item; // 获取首元结点的下一个结点,该节点即将变成新的首元结点 final Node<e> next = f.next; // 清空原首元结点的元素 f.item = null; // 清空原首元结点 f.next = null; // help GC // 将下一个结点设置成新的首元首元结点 first = next; // 如果下一个结点是 null,说明原来这个链表就只有一个元素,现在移除后就没有多的元素了,那么 last结点也需要设置为 null(其实这个时候移除的既是 first结点,又是 last结点) if(next == null) { last = null; } else { // 正常情况下,因为 linkedList是双向链表,所以需要设置首元结点的前指针为 null, next.prev = null; } size--; // 注意是结构化修改 modCount++; return element; }
E removeLast()
移除最后一个结点并且返回其元素
public E removeLast() { final Node<e> l = last; if(l == null) { throw new NoSuchElementException(); } return unlinkLast(l); } private E unlinkLast(Node<e> l) { // assert l == last && l != null final E element = l.element; final Node<e> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if(prev == null) { first = null; } else { prev.next = null; } size--; modCount++; return element; }
void addFirst(E e)
将指定元素插入到链表的头部
public void addFirst(E e) { // 方法定义在下面 linkFirst(e);}private void linkFirst(E e) { // 获取原首元结点 final Node<e> f = first; // 创建一个新结点,其 prev为 null, element为 e, next为 f(即原首元结点) final Node<e> newNode = new Node<e>(null, e, f); // 设置新结点为首元结点 first = newNode; // 如果原首元结点为 null,说明曾经该链表是空的,故需要设置尾结点也是新结点 if(f == null) { last = newNode; } else { // 正常情况下,设置元首元结点的前驱节点为新结点 f.prev = newNode; } size++; modCount++;}
void addLast(E e)
将指定的元素添加到链表的末尾
public void addLast(E e) { linkLast(e);}private void linkLast(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++;}
boolean contains(Object o)
返回结果为对链表内是否包含指定元素的判断
public boolean contains(Object o) { // 判断元素的索引值,如果为 -1则表示不存在,!=判断错误,返回 false;反之,返回 true return indexOf(o) != -1;}public int indexOf(Object o) { // 初始化索引下标 int index = 0; // 针对元素为 null进行遍历 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++; } } // 如果全部遍历完了也没找到,就返回 -1 return -1;}
int size()
返回链表中元素的个数
public int size() { // 返回实例中的 size字段 return size;}
boolean add(E e)
将传入的元素作为结点存到链表的尾部
public boolean add(E e) { // 和 addLast()方法一样,就多了一个返回值 linkLast(e); return true; }
boolean remove(Object o)
根据传入元素的值,移除链表中第一个值与之匹配的结点
如果该链表中不包含该元素,则不会做任何改变
public boolean remove(Object o) { if(o == null) { // 从首元结点开始遍历 for(Node<e> x = first; x != null; x = x.next) { if(x.item == null) { // 实际调用的移除方法 unlink(x); return true; } } } else { for(Node<e> x = first; x != null; x = x.next) { if(o.equals(x.item)) { unlink(x); return true; } } } return false; } E unlink(Node<e> x) { // assert x != null; final E element = x.element; // 获取当前结点的前驱和后继结点 final Node<e> next = x.next; final Node<e> prev = x.prev; // 如果前驱节点为空,说明当前结点就是首元结点,则只需要修改下一个结点为首元结点 if(prev == null) { first = next; } else { // 否则修改待删除结点的 前驱节点的后继 为 后继节点的前驱 prev.next = x.next; x.prev = null; } // 同理 if(next == null) { last = prev; } else { next.prev = x.prev; x.next = null; } x.item = null; size--; // 结构化修改 modCount++; return element; }
E remove(int index)
移除链表中处于指定索引位置的结点,并返回其中的元素
public E remove(int index) { // 检查索引的合理性 checkElementIndex(index); // 调用实际从链表中移除某个结点的方法 return unlink(node(index)); }
boolean addAll(Collection c)
将参数集合内的所有元素,添加到链表的末尾
添加的顺序依据该集合的迭代顺序
public boolean addAll(Collection<!--? extend E--> c) { // 将待插入的位置传过去,即调用下方的 return addAll(size, c);}
boolean addAll(int index, Collection c)
在指定位置之后插入集合内的元素
public boolean addAll(int index, Collection<!--? extends E--> c) { checkPositionIndex(index); // 将集合转换为数组,方便取用 Object[] a = c.toArray(); // 明确待添加的元素个数 int numNew = a.length; if(numNew == 0) { return false; } // 声明结点,pred为待插入位置的前一个结点;succ是待插入位置的后一个结点 Node<e> pred, succ; // 如果 index == size,则表示要在链表末端追加,此时尾结点即 pred,因为此时没有下一个结点,所以 succ = null if(index == size) { succ = null; pred = last; } else { // 依据所以获取结点,该结点现在的位置就是将来插入的结点的位置,所以该结点会成为新节点的下一个结点 succ = node(index); pred = succ.prev; } // 遍历数组中的元素 for(Object o : a) { // 将元素包裹成结点对象 E e = (E) o; // 此时新构造的结点已经指定了 前驱指针 指向的元素 Node<e> newNode = new Node<>(pred, e, null); // 如果前驱结点为 null,表明是一个空链表,新插入的结点即为首元结点 if(pred == null) { first = newNode; } else { // 否则,就正常插入,设置前驱结点的后继 为新插入结点 pred.next = newNode; } // 指针后移,获取下一个结点 pred = newNode; } // 当结点全部插入之后,最后插入的那个结点和其前驱的关系已经彻底解决,接下来解决和其后继之间的关系 // 如果 succ == null,则表明这是在链表末尾追加 if(succ == null) { // 设置 last结点为最后一个新插入结点 last = pred; } else { // 否则在最后一个新插入的结点和后继之间创建联系 pred.next = succ; succ.prev = pred; } // 更新链表长度 size += numNew; // 结构化修改 modCount++; return true; } // 返回在指定索引处的结点 Node<e> node(int index) { // assert isElementIndex(index) // 如果 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; } }
void clear()
清空链表里的所有元素,之后该链表将会变成一个空链表
public void clear() { // clearing all of the links between nodes is unnecessary // but it helps a generational GC if the discarded nodes inhabit more than one generation // is sure to free memory even if there is a reachable Iterator // 如果结点之间存在跨代引用(一个在新生代,另一个在老年代),那么删去链接能帮助 GC for(Node<e> x = first; x != null; ) { Node<e> next = x.next(); x.item = null; x.prev = null; x.next = null; x = next; } first = last = null; size = 0; modCount++;}
E get(int index)
返回链表中位于指定索引处的结点内存储的元素
public E get(int index) { // 检验索引是否有效 checkElementIndex(index); // 通过索引找到结点,返回结点中存储的元素 return node(index).item;}private void checkElementIndex(int index) { // 如果索引不合理就抛出异常 if(!isElementIndex(index)) { throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }}private boolean isElementIndex(int index) { return index >= 0 && index < size;}
E set(int index, E element)
在链表的指定索引处用指定的值替换其结点内存储的元素
public E set(int index, E element) { // 检查索引是否合理 checkElementIndex(index); // 通过索引获取结点 Node<e> x = node(index); // 改变结点内存储的元素内容 E oldVal = x.item; x.item = element; return oldVal;}
void add(int index, E element)
在链表的指定位置处插入一个元素
其他结点顺次后移一位
public void add(int index, E element) { // 和 checkElementIndex()的代码完全一样的,都是检查索引的合理性 checkPositionIndex(index); // 判断如果是插到队尾,就使用 linkLast() if(index == size) { linkLast(element); } else { // 获取当前 index的结点,插入到该结点前 linkBefore(element, node(index)); }}// 在指定结点前添加一个结点void linkBefore(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; // 如果 pred不存在,则表明 succ曾经是 first,现在 newNode在 succ之前,newNode就是 first了 if(pred == null) { first = newNode; } else { pred.next = newNode; } size++; modCount++;}
int lastIndexOf(Object o)
返回链表中值同参数一致的结点最后一次出现的索引位置
public int lastIndexOf(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; }
E peek()
获取链表的首元结点的元素(队列方法),首元结点不存在时,返回 null
public E peek() { final Node<e> f = first; // 避免直接返回 f.item时的空指针异常 return (f == null) ? null : f.item; }
E element()
获取链表的首元结点的元素(队列方法),首元结点不存在时,报错
public E element() { return getFirst(); }
E poll()
移除首元结点并返回其中存储的元素信息,如果链表为空,返回 null
public E poll() { final Node<e> f = first; return(f == null) ? null : unlinkFirst(f); }
E remove()
和上面那个方法差不多,只是如果链表为空,就报错
public E remove() { return removeFirst(); }
boolean offer(E e)
在链表的尾部添加元素
public boolean offer(E e) { return add(e); }
boolean offerFirst(E e)
在链队的头部添加元素(双端队列的方法)
public boolean offerFirst(E e) { addFirst(e); return true; }
boolean offerLast(E e)
在链表的尾部添加元素(双端队列的方法)
public boolean offerLast(E e) { addLast(e); return true; }
E peekFirst()
获取但不移除链表首元结点的元素,如果链表为空就返回 null
public E peekFirst() { final Node<e> f = first; return (f == null) ? null : f.item; }
E peekLast()
获取但不移除链表尾结点的元素,如果链表为空就返回 null
public E peekLast() { final Node<e> l = last; return (l == null) ? null : l.item; }
E pollFirst()
获取并且移除链表首元结点的元素,如果链表为空就返回 null
public E pollFirst() { final Node<e> f = first; return (f == null) ? null : unlinkFirst(f); }
E pollLast()
获取并移除链表尾结点的元素,如果链表为空就返回 null
public E pollLast() { final Node<e> l = last; return (l == null) ? null : unlinkLast(l); }
void push(E e)
将一个新的元素压入由链表表示的栈中,其实就是在链表头部插入,只是语义上算作是压栈的方法
public void push(E e) { addFirst(e); }
E pop()
将由链表表示的栈中的栈顶元素弹栈并返回其元素值,其实就是移除首元结点,语义上看坐弹栈
public E pop() { return removeFirst(); }
boolean removeFirstOccurence(Object o)
在从首元结点开始遍历链表时,移除指定元素第一次出现的那个结点,如果该结点不存在,则什么都不做
public boolean removeFirstOccurence(Object o) { return remove(o); }
boolean removeLastOccurence(Object o)
在从首元结点开始遍历链表时,移除指定元素最后一个出现的那个结点,如果该结点不存在,则什么都不
public boolean removeLastOccurrence(Object o) { if (o == null) { for (Node<e> x = last; x != null; x = x.prev) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<e> x = last; x != null; x = x.prev) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
ListIterator<e> listIterator(int index)
返回从某一个其实索引开始,由链表中元素组成的 列表迭代器
该迭代器是快速失败的,如果迭代时链表发生了不是由本迭代器进行的结构性修改,就会抛出一个 并发修改异常
public ListIterator<e> listIterator(int index) { // 检查索引合理性 checkPositionIndex(index); // 返回由该索引开始的列表迭代器 return new ListItr(index); }
Iterator<e> descendingIterator()
获取相对链表逆序的迭代器,迭代器中的第一个元素就是尾结点
public Iterator<e> descendingIterator() { return new DescendingIterator(); }
Object clone()
返回链表的浅拷贝,其中的结点及其元素没有拷贝,引用指向的是同一个内容
public Object clone() { // 先获得一个空壳子 LinkedList<e> clone = superClone(); // Put clone into "virgin" state clone.first = clone.last = null; clone.size = 0; clone.modCount = 0; // Initialize clone with our elements for(Node<e> x = first; x != null; x = x.next) { clone.add(x.item); } return clone; } private LinkedList<e> superClone() { try { return (LinkedList<e>) super.clone(); } catch(CloneNotSupportedException e) { throw new InternalError(e); } }
Object[] toArray()
返回一个包含列表所有元素的数组,并且同链表中一致的顺序排列
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; // 遍历每一个结点,取出后将内部元素赋值给数组元素 for(Node<e> x = first; x != null; x = x.next) { result[i++] = x.item; } return result; }
T[] toArray(T[] a)
同上,返回一个数组,但是类型和 元素类型一致
public <t> T[] toArray(T[] a) { if(a.length < size) { // 通过反射重新创建一个相同类型的数组 a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); } int i = 0; // result和 a指向同一个内存区域 Object[] result = a; for(Node<e> x = first; x != null; x = x.next) { result[i++] = x.item; } // 如果传入的数组的长度很长,就在最后一个元素之后设置一个 null if(a.length > size) { a[size] = null; } return a; }
Spliteratior<e> spliterator()
创建一个定长、有序的并行迭代器
public Spliterator<e> spliterator() { return new LLSpliterator<e>(list, -1, 0); }
常用内部类
private class ListItr implements ListIterator<e>
记得看到过一篇博客,批判了 next()方法里 lastReturned的赋值
// 列表专用的迭代器,运行程序员在任意方向上进行遍历和修改 // 它的 cursor指针总是位于调用 previous()返回的元素和 调用 next()返回的元素之间 // 所以针对 size为 n的列表,cursor的取值有 n+1个 // remove()和 set()方法的对象都不是 cursor,而是上一个由 next()或 previoust()返回的对象 private class ListItr implements ListIterator<e> { // 上一个返回的元素 private Node<e> lastReturned; // 下一个将返回的元素 private Node<e> next; //记录结点的索引位置 private int nextIndex; private int expectedModCount = modCount; // 根据索引创建迭代器,将从该索引之后的元素开始创建 ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index); nextIndex = index; } // 判断是否还有下个元素 public boolean hasNext() { return nextIndex < size; } // 获取下一个元素 public E next() { // 检查 modCount checkForComodification(); // 判断有没有下一个元素 if (!hasNext()) throw new NoSuchElementException(); // 获取将返回的元素设置成已经返回的元素 lastReturned = next; // 设置成下一个 next = next.next; nextIndex++; return lastReturned.item; } // 判断是否有前一个元素 public boolean hasPrevious() { return nextIndex > 0; } // 获取前一个元素 public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); // 获取带返回元素的上一个结点,但是之后,lastReturned 就等于 next了 lastReturned = next = (next == null) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex - 1; } // 移除一个元素 public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Node<e> lastNext = lastReturned.next; unlink(lastReturned); // 用过 previous()之后,二者就相等了,此时移除了 lastReturned的同时也移除了 next,所以需要对 next重新赋值 if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; } // 更新元素的值 public void set(E e) { if (lastReturned == null) throw new IllegalStateException(); checkForComodification(); lastReturned.item = e; } // 在当前迭代位置之后添加元素 public void add(E e) { checkForComodification(); lastReturned = null; // 链表空了 if (next == null) linkLast(e); else // 链表没空,新节点在 next之前 linkBefore(e, next); nextIndex++; expectedModCount++; } // 针对迭代器中的每一个元素都进行一次同一个操作 public void forEachRemaining(Consumer<!--? super E--> action) { Objects.requireNonNull(action); while (modCount == expectedModCount && nextIndex < size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
private static class Node<e>
// 链表的结点 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; } }
private class DescendingIterator implements Iterator<e>
// 逆序的迭代器 // 把原本的 hasPrevious()变成 hasNext(),把原本的 previous()改成 next() private class DescendingIterator implements Iterator<e> { private final ListItr itr = new ListItr(size()); public boolean hasNext() { return itr.hasPrevious(); } public E next() { return itr.previous(); } public void remove() { itr.remove(); } }
static final class LLSpliterator<e> implements Spliterator<e>
static final class LLSpliterator<e> implements Spliterator<e> { // 批量操作的单元个数,也就是说当前对象划分后至少应当遍历的大小 static final int BATCH_UNIT = 1 << 10; // batch array size increment // 最大单元的大小 static final int MAX_BATCH = 1 << 25; // max batch array size; // 当前集合 final LinkedList<e> list; // null OK unless traversed // 当前结点 Node<e> current; // current node; null until initialized // 预估规模大小 int est; // size estimate; -1 until first needed int expectedModCount; // initialized when est set // 已遍历的大小 int batch; // batch size for splits // 构造函数 LLSpliterator(LinkedList<e> list, int est, int expectedModCount) { this.list = list; this.est = est; this.expectedModCount = expectedModCount; } final int getEst() { int s; // force initialization final LinkedList<e> lst; // 如果还没有初始化,此时 est = -1 if ((s = est) < 0) { if ((lst = list) == null) s = est = 0; else { expectedModCount = lst.modCount; // current结点直接等于 LinkedList的 first属性,也就是说之后是从链表的头部开始遍历的 current = lst.first; s = est = lst.size; } } return s; } // 获得预估大小 public long estimateSize() { return (long) getEst(); } // 子划分遍历 public Spliterator<e> trySplit() { Node<e> p; // 初始化 est int s = getEst(); if (s > 1 && (p = current) != null) { int n = batch + BATCH_UNIT; // 如果 n超过了集合大小,就取集合最大值 if (n > s) n = s; // 如果 n超过了上限,就取上限 if (n > MAX_BATCH) n = MAX_BATCH; // 将链表中的元素放到数组中 Object[] a = new Object[n]; int j = 0; do { a[j++] = p.item; } while ((p = p.next) != null && j < n); // 当前结点等于遍历后的下一个结点 current = p; // batch等于子遍历的大小 batch = j; // 剩余估计大小需要减去已分配的值 est = s - j; // 返回一个子对象,内部本质还是基于数组的 return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED); } return null; } // 对每一个对象进行处理 public void forEachRemaining(Consumer<!--? super E--> action) { Node<e> p; int n; if (action == null) throw new NullPointerException(); // 初始化 est if ((n = getEst()) > 0 && (p = current) != null) { current = null; est = 0; // 从头开始遍历 do { E e = p.item; p = p.next; action.accept(e); } while (p != null && --n > 0); } if (list.modCount != expectedModCount) throw new ConcurrentModificationException(); } // 尝试预先处理 public boolean tryAdvance(Consumer<!--? super E--> action) { Node<e> p; if (action == null) throw new NullPointerException(); // 初始化 est,每消费一次,est的预估大小要减一 if (getEst() > 0 && (p = current) != null) { --est; E e = p.item; // 消费完毕后,current结点就是下一个结点了,一觉醒来又是美好的一天 current = p.next; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } return false; } // 表示该迭代器是有序、定长、子类也定长的 public int characteristics() { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; } }
</e></e></e></e></e></e></e></e>
这篇关于LinkedList源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-02Java管理系统项目实战入门教程
- 2024-11-02Java监控系统项目实战教程
- 2024-11-02Java就业项目项目实战:从入门到初级工程师的必备技能
- 2024-11-02Java全端项目实战入门教程
- 2024-11-02Java全栈项目实战:从入门到初级应用
- 2024-11-02Java日志系统项目实战:初学者完全指南
- 2024-11-02Java微服务系统项目实战入门教程
- 2024-11-02Java微服务项目实战:新手入门指南
- 2024-11-02Java项目实战:新手入门教程
- 2024-11-02Java小程序项目实战:从入门到简单应用