Collection框架之LinkedList
2015-11-19 15:32
176 查看
1.结构
LinkedList结构和ArrayList结构类似,也实现了List接口,因此LinkedList大多数方法名称我们已经很熟悉了.LinkedList和ArrayList有着一些重要的性能区别,例如,LinkedList没有ArrayList类的随机访问功能.下图为LinkedList部分方法图:
2.源码分析
2.1 类头
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
其中的Deque接口提供了双向队列需要实现的方法,例如:addFirst(E),addLast(E)等等.
2.2 字段
private static final long serialVersionUID = 876323262645176354L; transient int size = 0; transient Node<E> first; transient Node<E> last;
这其中size,first,last字段都是被transient关键词修饰的,这说明在序列化的时候这三个字段都不会被保存.由此看看LinkedList关于序列化的方法.
private void writeObject(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); } @SuppressWarnings("unchecked") private void readObject(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()); }
可以看出,LinkedList在进行序列化的时候只保存了集合元素的大小和元素本身的值而已,反序列化时通过迭代的方式重新创建集合元素.
在上述字段中的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; } }
Node代表LinkedList中的一个元素,item为值,next为下一个元素的引用,prev为上一个元素的引用.
2.3 构造器
public LinkedList() { } public LinkedList(Collection<? extends E> c) { this(); addAll(c); } public boolean addAll(Collection<? extends E> c) { return addAll(size, c); }
共有2个方法,其中的addAll方法在后面会提到.在这里的作用是在下标为size的位置添加集合元素,由于新建的LinkedList的size=0,所以就由Collection创建了链表.
2.4 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 = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
该方法只要的场景如下,首先最开始新创建时first = last = null,first和last的Node结构图如下:
当执行add(E)添加一个元素后,创建一个新的newNode节点元素,并指定之前元素为last,之后元素为null,并将last指向新元素,代表新加的元素为最后一个.最后通过判断 first和last都指向了newNode.此时first和last的Node结构图如下:
再执行add操作后,结构图为:
从这里看出LinkedList并没有设计成循环列表.
因为add方法没有last.next = first; first.prev = last;
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; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
addAll方法先查找需要插入位置的前一个元素,通过迭代将pred.next和newNode进行绑定,最后将原本位置的元素succ.prev设置成最后一个插入的元素.
2.5 remove方法
public E remove(int index) { checkElementIndex(index); return unlink(node(index)); }
先看node方法:
Node<E> node(int index) { // assert isElementIndex(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; } }
node方法实际是从中间位置开始搜索的,不过由于循环的存在,搜索的时间是跟长度成正比的.
unlink方法:
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; }
解除绑定的原理也很简单,就是将指定位置的元素的prev.next指向next,再将指定位置的元素的next.prev指向prev.最后将指定元素致空,方面GC回收.
2.6 indexOf方法
public int indexOf(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; }
LinkedList的indexOf方法也是通过循环的方式进行比较,因此搜索的时间是跟长度成正比的.
其余的方法与其上的方法类似,这里不再举出.
3.总结
LinkedList和ArrayList有着许多相似之处,都继承List接口,都有增删改查操作,而且用起来也差不多,关键在于性能不同.在上述方法中我们已经察觉到了,LinkedList的add,remove方法在时间数量级上是个常数,而ArrayList在使用add,remove方法时需要扩容或者 使用System.arraycopy方法,时间数量级上并不是个常数,而是跟n成正比.
但在索引位置的时候,LinkedList用过循环遍历的方式,时间数量级上和n成正比,而ArrayList只需简单的elementData[index]就可以了.
相关文章推荐
- python logging模块实例
- ubuntu的终端密码忘记或者出现认证失败
- 如何架设流媒体服务器
- 【HDU5544 2015CCPC 南阳国赛E】【树上dfs找本质不同环 高斯消元 时间戳优化】Ba Gua Zhen 连通图上最大异或环
- Lesson: The "Hello World!" Application
- 分享29个基于Bootstrap的HTML5响应式网页设计模板
- windows下如何玩linux命令
- Netty:ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter
- android上的JAVA8:使用retrolambda
- C语言中的回调
- 关于PDO防sql注入问题
- floyd最小环 详细讲解
- The value (166) of MAXTRANS parameter ignored.
- jsp中将后台传递过来的json格式的list数据绑定到下拉菜单select
- 窗口宽高 滚动条滚动距离 元素的文档坐标和窗口坐标
- css 实现三角形的原理
- Jenkins+appium+testng持续集成
- 设计一款给爸爸妈妈用的手机
- Android编程实现Gallery中每次滑动只显示一页的方法
- HashMap与ConcurrentHashMap的区别