LinkedList源码解析
来源:互联网 发布:php shell exec 输出 编辑:程序博客网 时间:2024/06/05 11:33
LinkedList源码解析
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
继承了AbstractSequentialList<E>
类,实现了List<E>, Deque<E>, Cloneable, java.io.Serializable
4个接口,其中Cloneable, java.io.Serializable是标记接口
注:Java中常用的三个标记接口分别是:RandomAccess、Cloneable、Serializable
java.io.Serializable这个接口是用来标记类是否支持序列化的,所谓的序列化就是将对象的各种信息转换成可以存储或者传输的一种形式。如果一个类没有实现该接口,却被拿去序列化的了,那么虚拟机就会抛出不支持序列化的异常。
Cloneable它的作用是标记该对象是否拥有克隆的能力
RandomAccess这个接口的作用是判断集合是否能快速访问,也就是通过索引下标能否快速的移动到对应的元素上
这是因为LinkedList没实现RandomAccess接口所有他的随机查找效率低
private transient Entry<E> header = new Entry<E>(null, null, null); private transient int size = 0;
这是LinkedList的2个属性,header相当于是头节点,Entry相当于节点对象,size是集合里元素的个数
private static class Entry<E> { E element; Entry<E> next; Entry<E> previous; Entry(E element, Entry<E> next, Entry<E> previous) { this.element = element; this.next = next; this.previous = previous; } }
这是Entry对象的源码, 只定义了存储的元素、前一个元素、后一个元素,这就是双向链表的节点的定义,每个节点只知道自己的前一个节点和后一个节点。
public LinkedList() { header.next = header.previous = header; } public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
这2个是构造方法,第一个是创建一个空的链表,第二个则是在第一个的基础上,将集合C加入链表中
其中addAll(C)方法有2种我们这用的是第一种
public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } public boolean addAll(int index, Collection<? extends E> c) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); Object[] a = c.toArray(); int numNew = a.length; if (numNew==0) return false; modCount++; Entry<E> successor = (index==size ? header : entry(index)); Entry<E> predecessor = successor.previous; for (int i=0; i<numNew; i++) { Entry<E> e = new Entry<E>((E)a[i], successor, predecessor); predecessor.next = e; predecessor = e; } successor.previous = predecessor; size += numNew; return true; }
第一种很简单就是简单的将集合C加入链表中,第二种则是要设置一个下标,让我们从该下标出将集合C加入到链表中,这是受影响的只有插入位置的前后2个元素,他们的前一项或者后一项的地址会发生改变,其他元素不受影响,这也是为什么LinkedList比ArrayList的插入和删除效率高的原因所在
public E getFirst() { if (size==0) throw new NoSuchElementException(); return header.next.element; } public E getLast() { if (size==0) throw new NoSuchElementException(); return header.previous.element; } public E get(int index) { return entry(index).element; }
这三个方法,作用在于取得第一个元素,最后一个元素,以及通过下标查找相对应的元素
我们这么理解header相当于起点,header.next.element相当于第一个元素,header.previous.element相当于最后一个元素,至于第3个则是通过以下方法实现的
private Entry<E> entry(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); Entry<E> e = header; if (index < (size >> 1)) { for (int i = 0; i <= index; i++) e = e.next; } else { for (int i = size; i > index; i--) e = e.previous; } return e; }
size >> 1右移一位,结果相当于size/2
entry(int index)方法的意思是:
如果index小于0或者大于等于size,则抛出一个异常
如果index在链表的前半段((index < (size >> 1)),则从链表的头开始,从前往后找
否则(index在链表的后半段),则从链表的尾部开始,从后往前找
public E removeFirst() { return remove(header.next); } public E removeLast() { return remove(header.previous); }
这2个方法是用于删除第一个元素和最后一个元素其实际调用的是以下方法
private E remove(Entry<E> e) { if (e == header) throw new NoSuchElementException(); //保留被移除的元素:要返回 E result = e.element; //将该节点的前一节点的next指向该节点后节点 e.previous.next = e.next; //将该节点的后一节点的previous指向该节点的前节点 //这两步就可以将该节点从链表从除去:在该链表中是无法遍历到该节点的 e.next.previous = e.previous; //将该节点归空 e.next = e.previous = null; size--; modCount++; return result; }
public boolean remove(Object o) { if (o==null) { for (Entry<E> e = header.next; e != header; e = e.next) { if (e.element==null) { remove(e); return true; } } } else { for (Entry<E> e = header.next; e != header; e = e.next) { if (o.equals(e.element)) { remove(e); return true; } } } return false; }
这个方法,相当于找到相对应的对象,如果相等就删除。
public E remove(int index) { return remove(entry(index)); }
这个方法分2步走,第一步先通过entry(index)找到对应的E对象,然后删除对象
public boolean removeFirstOccurrence(Object o) { return remove(o); } public boolean removeLastOccurrence(Object o) { if (o==null) { for (Entry<E> e = header.previous; e != header; e = e.previous) { if (e.element==null) { remove(e); return true; } } } else { for (Entry<E> e = header.previous; e != header; e = e.previous) { if (o.equals(e.element)) { remove(e); return true; } } } return false; }
这2个方法的作用是将第一次出现,最后一次出现的指定对象删除。
第一个相当于调用的remove(Object o)方法,第二个跟remove(Object o)相似,只不过他的查找顺序是从后往前查找
public void addFirst(E e) { addBefore(e, header.next); } public void addLast(E e) { addBefore(e, header); } public boolean add(E e) { addBefore(e, header); return true; } public void add(int index, E element) { addBefore(element, (index==size ? header : entry(index))); }
这里4个都是添加方法但是都用到的了addBefore方法,所以我们先介绍一下addBefore方法
private Entry<E> addBefore(E e, Entry<E> entry) { Entry<E> newEntry = new Entry<E>(e, entry, entry.previous); newEntry.previous.next = newEntry; newEntry.next.previous = newEntry; size++; modCount++; return newEntry; }
这个方法,通过传入一个新节点,以及要插入的位置的节点,先将新节点的前后关联起来,将原来的节点最为新节点的下一个元素,原来节点的上一个元素,作为新节点的上一个元素,然后在将3个节点重新关联起来,完成插入,了解了这个方法,以上的4个添加方法也就明白了
public boolean contains(Object o) { return indexOf(o) != -1; }
这个方法是用来确认是否存在这个元素,实际上是使用了indexOf(o)方法
public int indexOf(Object o) { int index = 0; if (o==null) { for (Entry e = header.next; e != header; e = e.next) { if (e.element==null) return index; index++; } } else { for (Entry e = header.next; e != header; e = e.next) { if (o.equals(e.element)) return index; index++; } } return -1; } public int lastIndexOf(Object o) { int index = size; if (o==null) { for (Entry e = header.previous; e != header; e = e.previous) { index--; if (e.element==null) return index; } } else { for (Entry e = header.previous; e != header; e = e.previous) { index--; if (o.equals(e.element)) return index; } } return -1; }
这个方法大家会发现和remove(Object o)很相似,只不过这里不用移除,而是返回下标,只要下标不返回-1
之前的方法返回的都是TRUE 反之则是FALSE
第二个方法与第一个刚好相反,查找方向不同其他完全一样
public int size() { return size; }
这个不多说,就是返回集合的大小
public void clear() { Entry<E> e = header.next; while (e != header) { Entry<E> next = e.next; e.next = e.previous = null; e.element = null; e = next; } header.next = header.previous = header; size = 0; modCount++; }
这是将集合清空,从头开始清
public E get(int index) { return entry(index).element; } public E set(int index, E element) { Entry<E> e = entry(index); E oldVal = e.element; e.element = element; return oldVal; }
这2个方法则是通过下标得到元素,以及设置元素,后者通过下标找到位置后,用新的数据更换旧的数据即可
private class ListItr implements ListIterator<E> { 2 // 最近一次返回的节点,也是当前持有的节点 3 private Entry<E> lastReturned = header; 4 // 对下一个元素的引用 5 private Entry<E> next; 6 // 下一个节点的index 7 private int nextIndex; 8 private int expectedModCount = modCount; 9 // 构造方法,接收一个index参数,返回一个ListItr对象 10 ListItr(int index) { 11 // 如果index小于0或大于size,抛出IndexOutOfBoundsException异常 12 if (index < 0 || index > size) 13 throw new IndexOutOfBoundsException("Index: "+index+ 14 ", Size: "+size); 15 // 判断遍历方向 16 if (index < (size >> 1)) { 17 // next赋值为第一个节点 18 next = header.next; 19 // 获取指定位置的节点 20 for (nextIndex=0; nextIndex<index; nextIndex++) 21 next = next.next; 22 } else { 23 // else中的处理和if块中的处理一致,只是遍历方向不同 24 next = header; 25 for (nextIndex=size; nextIndex>index; nextIndex--) 26 next = next.previous; 27 } 28 } 29 // 根据nextIndex是否等于size判断时候还有下一个节点(也可以理解为是否遍历完了LinkedList) 30 public boolean hasNext() { 31 return nextIndex != size; 32 } 33 // 获取下一个元素 34 public E next() { 35 checkForComodification(); 36 // 如果nextIndex==size,则已经遍历完链表,即没有下一个节点了(实际上是有的,因为是循环链表,任何一个节点都会有上一个和下一个节点,这里的没有下一个节点只是说所有节点都已经遍历完了) 37 if (nextIndex == size) 38 throw new NoSuchElementException(); 39 // 设置最近一次返回的节点为next节点 40 lastReturned = next; 41 // 将next“向后移动一位” 42 next = next.next; 43 // index计数加1 44 nextIndex++; 45 // 返回lastReturned的元素 46 return lastReturned.element; 47 } 48 49 public boolean hasPrevious() { 50 return nextIndex != 0; 51 } 52 // 返回上一个节点,和next()方法相似 53 public E previous() { 54 if (nextIndex == 0) 55 throw new NoSuchElementException(); 56 57 lastReturned = next = next.previous; 58 nextIndex--; 59 checkForComodification(); 60 return lastReturned.element; 61 } 62 63 public int nextIndex() { 64 return nextIndex; 65 } 66 67 public int previousIndex() { 68 return nextIndex-1; 69 } 70 // 移除当前Iterator持有的节点 71 public void remove() { 72 checkForComodification(); 73 Entry<E> lastNext = lastReturned.next; 74 try { 75 LinkedList.this.remove(lastReturned); 76 } catch (NoSuchElementException e) { 77 throw new IllegalStateException(); 78 } 79 if (next==lastReturned) 80 next = lastNext; 81 else 82 nextIndex--; 83 lastReturned = header; 84 expectedModCount++; 85 } 86 // 修改当前节点的内容 87 public void set(E e) { 88 if (lastReturned == header) 89 throw new IllegalStateException(); 90 checkForComodification(); 91 lastReturned.element = e; 92 } 93 // 在当前持有节点后面插入新节点 94 public void add(E e) { 95 checkForComodification(); 96 // 将最近一次返回节点修改为header 97 lastReturned = header; 98 addBefore(e, next); 99 nextIndex++;100 expectedModCount++;101 }102 // 判断expectedModCount和modCount是否一致,以确保通过ListItr的修改操作正确的反映在LinkedList中103 final void checkForComodification() {104 if (modCount != expectedModCount)105 throw new ConcurrentModificationException();106 }107 }
public Iterator<E> descendingIterator() { return new DescendingIterator(); } /** Adapter to provide descending iterators via ListItr.previous */ private class DescendingIterator implements Iterator { final ListItr itr = new ListItr(size()); public boolean hasNext() { return itr.hasPrevious(); } public E next() { return itr.previous(); } public void remove() { itr.remove(); } }
从类名和上面的代码可以看出这是一个反向的Iterator,代码很简单,都是调用的ListItr类中的方法。
public Object clone() { LinkedList<E> clone = null; try { clone = (LinkedList<E>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } // Put clone into "virgin" state clone.header = new Entry<E>(null, null, null); clone.header.next = clone.header.previous = clone.header; clone.size = 0; clone.modCount = 0; // Initialize clone with our elements for (Entry<E> e = header.next; e != header; e = e.next) clone.add(e.element); return clone; }
调用父类的clone()方法初始化对象链表clone,将clone构造成一个空的双向循环链表,之后将header的下一个节点开始将逐个节点添加到clone中。最后返回克隆的clone对象
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Entry<E> e = header.next; e != header; e = e.next) result[i++] = e.element; return result; }public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Entry<E> e = header.next; e != header; e = e.next) result[i++] = e.element; if (a.length > size) a[size] = null; return a; }
第一个将创建大小和LinkedList相等的数组result,遍历链表,将每个节点的元素element复制到数组中,返回数组。
第二个 先判断出入的数组a的大小是否足够,若大小不够则拓展。这里用到了发射的方法,重新实例化了一个大小为size的数组。之后将数组a赋值给数组result,遍历链表向result中添加的元素。最后判断数组a的长度是否大于size,若大于则将size位置的内容设置为null。返回a。
从代码中可以看出,数组a的length小于等于size时,a中所有元素被覆盖,被拓展来的空间存储的内容都是null;若数组a的length的length大于size,则0至size-1位置的内容被覆盖,size位置的元素被设置为null,size之后的元素不变。
简单来说,如果新创建的数组大小小与元集合大小,则会自动拓展,反之,多出来的位置则由NULL填充
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 (Entry e = header.next; e != header; e = e.next) s.writeObject(e.element); } 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(); // Initialize header header = new Entry<E>(null, null, null); header.next = header.previous = header; // Read in all elements in the proper order. for (int i=0; i<size; i++) addBefore((E)s.readObject(), header); }
这2个方法用于,集合的传输
public E peek() { if (size==0) return null; return getFirst(); } public E element() { return getFirst(); }
2个方法都是返回集合第一个元素
public E poll() { if (size==0) return null; return removeFirst(); } public E remove() { return removeFirst(); }
这2个方法的作用是移除第一个元素
public boolean offer(E e) { return add(e); } public boolean offerFirst(E e) { addFirst(e); return true; } public boolean offerLast(E e) { addLast(e); return true; } public void push(E e) { addFirst(e); }
3个方法,第一个和第三个作用相同即添加元素到最后一个,第二 四个方法则是添加元素到第一个位置
public E peekFirst() { if (size==0) return null; return getFirst(); } public E peekLast() { if (size==0) return null; return getLast(); }
作用分别是返回第一个元素和最后一个元素
public E pollFirst() { if (size==0) return null; return removeFirst(); } public E pollLast() { if (size==0) return null; return removeLast(); } public E pop() { return removeFirst(); }
作用分别是移除第一个元素和最后一个元素,第三个也是移除第一个元素
- linkedList 源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- ArrayList LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList集合源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- LinkedList源码解析
- 从源码解析LinkedList
- LinkedList源码解析
- deepin15.4.1开机logo显示完毕后系统黑屏解决方案
- quartz中的corn表达式
- dede标签:arclist标签使用大全
- 面试题总结
- 基于Dragonboard 410c的总线控制之SPI(一)
- LinkedList源码解析
- Linux下的lds链接脚本基础
- JavaWeb三大组件——过滤器的运行机制理解
- SSH整合配置步骤
- 实现表格隔行/点击换色效果
- 第七周项目3
- for编写九九乘法表
- 计算单词个数
- 数字金融监管