Java基础集合类(二):LinkedList详解
来源:互联网 发布:nginx 查看模块 编辑:程序博客网 时间:2024/03/29 05:02
LinkedList详解
1、简介
LinkedList是一个双向链表,允许null元素存储,内部是使用Node节点来相互链接的,每个Node节点都有一个向前指针、向后指针和当前存储元素,详细代码如下:
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; } }LinkedList有两个属性,一个是first,指向第一个Node节点,一个是last,指向最后一个节点,因此LinkedList在访问头节点或者尾节点的时候速度很快,还有一个size属性,用于记录当前存储的元素个数。LinkedList继承与AbstractList,因此从父类中继承到了modCount属性,这个可以在迭代时判断链表结构是否被改变,例如add,remove等方法都会改变这个属性,若迭代时链表结构改变,则会抛出ConcurrentModificationException异常。
LinkedList是非线程安全的,因此在多线程环境下使用时,需要注意同步问题。LinkedList不仅实现了List接口,同时也实现了Deque接口,因此LinkedList也可以作为Stack栈以及Queue队列来使用,只不过LinkedList在作为栈以及队列时,效率不如ArrayDeque高,以后我会做一下对比。
2、构造器
LinkedList构造器只有两个,一个是无参构造器,一个是有参构造器LinkedList(Collection<? extends E> c),有参构造器是使用一个集合来构造一个LinkedList,内部使用的是addAll(Collection<? extends E> c)方法来实现,这个在下面讲关键方法时会讲到。3、关键方法
下面讲一下一些主要的关键方法,好多方法都是重用的,把这些关键方法理解了,其他的方法也就好理解了。链表主要是插入和移除操作,其中插入可以在头节点、尾节点、中间插入,移除可以在头节点、尾节点和中间移除。3.1、linkLast(E e)
这是一个包访问权限的方法,方法的主要功能是把e元素链接到链表的尾部。大概实现:尾节点l,头节点指针first,尾节点指针last,新节点newNode,newNode.prev指向l,last指向newNode,如果l为空,则表示当前链表是空的,此时还需要把first指针指向newNode,l不为空,则l.next指向newNode。
使用到此方法的关键方法:addLast(E e),add(E e),add(int index, E element),LinkedList的反序列化方法readObject(java.io.ObjectInputStream s)也会用到。其他的比如像offer、offerLast等方法,是直接使用add、addLast方法的,因此也相当于变相使用linkLast方法。
关键代码如下:
void linkLast(E e) { final Node<E> l = last;//获取尾节点引用 final Node<E> newNode = new Node<>(l, e, null);//新节点prev指向l last = newNode;//last指针指向新节点 if (l == null) first = newNode;//l为空则需要first也指向新节点 else l.next = newNode;//l的下一个节点指向新节点 size++; modCount++; }
3.2、linkFirst(E e)
这是一个私有访问权限的方法,方法的主要功能是把e元素插入到链表的头部。大概实现:头节点f,头节点指针first,尾节点指针last,新节点newNode,newNode.next指向f,first指向newNode,若f为空,则表示链表是空的,last也需要指向newNode,f不为空,则f.prev指向newNode。
使用到此方法的关键方法:addFirst(E e),offerFirst、push有使用到addFirst方法。
关键代码如下:
private void linkFirst(E e) { final Node<E> f = first;//获取头节点的引用 final Node<E> newNode = new Node<>(null, e, f);//新节点的next指向f first = newNode; if (f == null) last = newNode; else f.prev = newNode;//原头节点的prev指向新节点 size++; modCount++; }
3.3、linkBefore(E e, Node<E> succ)
这是一个包访问权限的方法,方法的主要功能是在succ节点前插入e元素。大概实现:succ前节点是prev,头节点指针first,新节点newNode,newNode.prev指向prev节点,newNode.next指向succ,如果prev是null,表示succ是头节点,此时需要first指向newNode,prev不为空,则prev.next指向newNode,succ.prev指向newNode。使用到此方法的关键方法:add(int index, E element),链表实现的迭代器中的add(E e)也有使用到。
关键代码如下:
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; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
3.4、unlinkLast(Node<E> l)
这是一个私有访问权限的方法,方法的主要功能是移除末尾节点l。大概实现:l的前节点prev,last指针指向prev,如果prev是null,则表示当前链表已经空了,则first=null,如果prev不为空,则prev.next=null。
使用到此方法的关键方法:removeLast(),pollLast()。
关键代码如下:
private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; 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; }
3.5、unlinkFirst(Node<E> f)
这是一个私有访问权限的方法,方法的主要功能是移除头节点f。大概实现:f的后一个节点是next,first指针指向next,如果next为null,则表示当前链表已经空了,last=null,如果next不为空,则next.prev=null。使用到此方法的关键方法:removeFirst(),poll(),pollFirst(),remove(),pop()等。
关键代码如下:
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; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
3.6、unlink(Node<E> x)
这是一个包访问权限的方法,方法的主要功能是移除节点x。大概实现:x的前节点prev,后节点next,x.preve=null,x.next=null,prev为空,则first=next,prev不为空,则prev.next=next;next为空,则last=prev,next不为空,则next.prev=prev。使用到此方法的关键方法:remove(Object o),remove(int index),removeLastOccurrence(Object o),LinkedList的列表迭代器中的remove()方法也有使用到。
关键代码如下:
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; }
3.7、node(int index)
这是一个包访问权限的方法,方法的主要功能是找到index索引位置的元素。这个方法的实现并不是迭代全部的链表元素来查找元素,而是先用index与size的1/2来进行比较,如果小于则从链表的左边来迭代查找,范围是[0,index],如果大于则从链表的右边来迭代查找,范围是[index,size-1]。这个方法比较重要,因为LinkedList是使用双向链表来存储元素的,并不像数组一样可以方便的使用index索引来进行访问,使用此方法能比较方便的查找到index索引位置的元素。例如移除指定索引的元素remove(int index)方法,就需要先使用此方法来查找到index对应的元素,然后再调用unlink(Node<E> x)方法来移除元素。关键代码如下:
Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) {//与size的1/2进行对比 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; } }
3.8、addAll(int index, Collection<? extends E> c)
把指定的集合c插入的指定的index索引位置,跟add(int index, E element)方法类似,根据index找到对应的node,然后在node和prev之间插入集合c的所有元素。
3.9、clear()
清空链表,需要迭代链表,把所有Node中的prev、next、item置为null,帮助垃圾回收。关键代码如下:
public void clear() { for (Node<E> x = first; x != null; ) { Node<E> next = x.next; x.item = null; x.next = null; x.prev = null; x = next; } first = last = null; size = 0; modCount++; }
3.10、indexOf(Object o)
获取o元素的索引,不存在则返回-1,内部实现是迭代所有元素,逐一进行equals。3.11、toArray(T[] a)
把链表所有元素转换成泛型T数组,内部实现有一个创建数组的小技巧,使用Array.newInstance(Class<?> componentType, int length)方法可以创建泛型数组,这个是使用到了反射的技巧。4、总结
LinkedList是一个双向链表,内部使用Node节点来包装元素,并且指向前节点和后节点,在插入元素时,构建一个新的Node节点并修改前节点和后节点的指针,因此LinkedList的随机插入效率相对会高一些,但是根据索引来随机访问并不像数组那么方便,LinkedList需要进行遍历来寻找索引位置的元素,因此LinkedList的随机访问效率相对会低一些。LinkedList的插入和删除操作大量重用了一些方法,这些方法无非就是头节点插入、尾节点插入、中间节点插入、头节点移除、尾节点移除、中间节点移除这些,在上面的关键方法中已经讲解过。- Java基础集合类(二):LinkedList详解
- Java集合类详解-LinkedList
- 【JAVA集合详解】LinkedList
- Java集合源码分析(二)Linkedlist
- java集合二之LinkedList
- Java基础--集合List-LinkedList
- Java集合类--LinkedList
- Java集合类--LinkedList
- java集合类LinkedList
- Java集合(LinkedList)
- 【Java基础之集合(二)】Java中HashMap详解
- Java基础之集合框架详解(二)List篇
- JAVA集合二 ——list(04 LinkedList)
- 【JAVA基础】集合类源码分析_LinkedList
- Java基础--集合框架Conllection(LinkedList、ArrayList)
- java基础(集合List-ArrayList、LinkedList、Vector的区别)
- java集合类(四)LinkedList应用
- Java基础(二)之集合类
- 国庆清北刷题冲刺班 Day5 上午
- 不带头节点的单链表及其基本操作(Java实现)
- Gauge中文文档(10)—故障处理
- IIS与IIC
- imageLoader框架
- Java基础集合类(二):LinkedList详解
- 不爱说话?社交无能?或许你可以试试这种说话术
- 2017下半年ACM-ICPC网络赛签到题汇总
- 带头节点的双链表及其基本操作(Java实现)
- PTA 7-7(排序) Windows消息队列(25 分) 25分代码 优先队列
- windows查看某个端口被谁占用
- 嵌入式视频处理基础(二)
- 编程珠玑--啊哈!算法
- POJ2096 Collecting Bugs (概率DP)