这期内容当中小编将会给大家带来有关java中LinkedList有什么用,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
底层数据结构是一个双向链表、适用于有顺序迭代的场景
first:头节点
last:尾节点
当链表为空时,last、first都是同一个节点,前后都指向null
因为是双向链表,只要机器内存足够强大,是没有大小限制的
node有是三个属性:prev、next、item
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; } }
一、新增、删除
头部新增:addFirst
// 从头部追加 private void linkFirst(E e) { // 头节点赋值给临时变量 final Node<E> f = first; // 新建节点,前一个节点指向null,e 是新建节点,f 是新建节点的下一个节点,目前值是头节点的值 final Node<E> newNode = new Node<>(null, e, f); // 新建节点成为头节点 first = newNode; // 头节点为空,就是链表为空,头尾节点是一个节点 if (f == null) last = newNode; //上一个头节点的前一个节点指向当前节点 else f.prev = newNode; size++; modCount++; }
尾部新增:add
// 从尾部开始追加节点 void linkLast(E e) { // 把尾节点数据暂存 final Node<E> l = last; // 新建新的节点,初始化入参含义: // l 是新节点的前一个节点,当前值是尾节点值 // e 表示当前新增节点,当前新增节点后一个节点是 null final Node<E> newNode = new Node<>(l, e, null); // 新建节点追加到尾部 last = newNode; //如果链表为空(l 是尾节点,尾节点为空,链表即空),头部和尾部是同一个节点,都是新建的节点 if (l == null) first = newNode;![图片描述](//img.mukewang.com/5d5fc69600013e4803600240.gif) //否则把前尾节点的下一个节点,指向当前尾节点。 else l.next = newNode; //大小和版本更改 size++; modCount++; }
删除
与新增类似、可以头部删除unlinkFirst、可以尾部删除unlinkLast
//从头删除节点 f 是链表头节点 private E unlinkFirst(Node<E> f) { // 拿出头节点的值,作为方法的返回值 final E element = f.item; // 拿出头节点的下一个节点 final Node<E> next = f.next; //帮助 GC 回收头节点 f.item = null; f.next = null; // 头节点的下一个节点成为头节点 first = next; //如果 next 为空,表明链表为空 if (next == null) last = null; //链表不为空,头节点的前一个节点指向 null else next.prev = null; //修改链表大小和版本 size--; modCount++; return element; }
查询
LinkedList采用二分法进行循环查询(平时可借鉴)
// 根据链表索引位置查询节点 Node<E> node(int index) { // 如果 index 处于队列的前半部分,从头开始找,size >> 1 是 size 除以 2 的意思。 if (index < (size >> 1)) { Node<E> x = first; // 直到 for 循环到 index 的前一个 node 停止 for (int i = 0; i < index; i++) x = x.next; return x; } else {// 如果 index 处于队列的后半部分,从尾开始找 Node<E> x = last; // 直到 for 循环到 index 的后一个 node 停止 for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
二、迭代器
ListIterator:提供了向前、向后的迭代方式
// 双向迭代器 private class ListItr implements ListIterator<E> { private Node<E> lastReturned;//上一次执行 next() 或者 previos() 方法时的节点位置 private Node<E> next;//下一个节点 private int nextIndex;//下一个节点的位置 //expectedModCount:期望版本号;modCount:目前最新版本号 private int expectedModCount = modCount; ………… }
从头到尾:
// 判断还有没有下一个元素 public boolean hasNext() { return nextIndex < size;// 下一个节点的索引小于链表的大小,就有 } // 取下一个元素 public E next() { //检查期望版本号有无发生变化 checkForComodification(); if (!hasNext())//再次检查 throw new NoSuchElementException(); // next 是当前节点,在上一次执行 next() 方法时被赋值的。 // 第一次执行时,是在初始化迭代器的时候,next 被赋值的 lastReturned = next; // next 是下一个节点了,为下次迭代做准备 next = next.next; nextIndex++; return lastReturned.item; }
从尾到头:
// 如果上次节点索引位置大于 0,就还有节点可以迭代 public boolean hasPrevious() { return nextIndex > 0; } // 取前一个节点 public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); // next 为空场景:1:说明是第一次迭代,取尾节点(last);2:上一次操作把尾节点删除掉了 // next 不为空场景:说明已经发生过迭代了,直接取前一个节点即可(next.prev) lastReturned = next = (next == null) ? last : next.prev; // 索引位置变化 nextIndex--; return lastReturned.item; }
删除:
public void remove() { checkForComodification(); // lastReturned 是本次迭代需要删除的值,分以下空和非空两种情况: // lastReturned 为空,说明调用者没有主动执行过 next() 或者 previos(),直接报错 // lastReturned 不为空,是在上次执行 next() 或者 previos()方法时赋的值 if (lastReturned == null) throw new IllegalStateException(); Node<E> lastNext = lastReturned.next; //删除当前节点 unlink(lastReturned); // next == lastReturned 的场景分析:从尾到头递归顺序,并且是第一次迭代,并且要删除最后一个元素的情况下 // 这种情况下,previous() 方法里面设置了 lastReturned = next = last,所以 next 和 lastReturned会相等 if (next == lastReturned) // 这时候 lastReturned 是尾节点,lastNext 是 null,所以 next 也是 null,这样在 previous() 执行时,发现 next 是 null,就会把尾节点赋值给 next next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; }
上述就是小编为大家分享的java中LinkedList有什么用了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。