`

源码阅读之LinkedList

阅读更多
LinkedList类似C语言的双向链表,但是java中没有指针如何实现呢,看完LinkedList
  你将对java中的引用类型有更深入的理解。LindedList的声明如下:
  
public class LinkedList extends AbstractSequentialList implements List, Cloneable, java.io.Serializable


  下面分析一下这个链表的实现,这里只重点分析某些方法。
  
private transient Entry header = new Entry(null, null, null);
  private transient int size = 0;
  public LinkedList() {
  header.next = header.previous = header;//previous 和 next都指向自身
  }

  header相当于C中的头指针,而且这个头指针不是链表的元素,有关Entry将在下面介绍。
 
 public LinkedList(Collection c) {
  this();
  addAll(c);
  }
  public Object getFirst() {
  if (size==0)
  throw new NoSuchElementException();
   return header.next.element;
  }

  头指针的下一个元素就是第一个元素
  public Object getLast() {
  if (size==0)
  throw new NoSuchElementException();
  return header.previous.element;
  }
  头指针的前一个当然就是最后一个了
 
 public Object removeFirst() {
  Object first = header.next.element;
  remove(header.next);
  return first;
  }
  public Object removeLast() {
  Object last = header.previous.element;
  remove(header.previous);
  return last;
  }
  public void addFirst(Object o) {
  addBefore(o, header.next);
  }
  public void addLast(Object o) {
  addBefore(o, header);
  }

  这个方法在链表末尾插入新的元素,功能和add方法一样,这个方法完全是为了对称性(因为有addFirst)
 
 public boolean contains(Object o) {
  return indexOf(o) != -1;
  }
  public int size() {
  return size;
  }
  public boolean add(Object o) {
  addBefore(o, header);
  return true;
  }
  public boolean remove(Object o) {
  if (o==null) {
  for (Entry e = header.next; e != header; e = e.next) {
  if (e.element==null) {
  remove(e);
  return true;
  }
  }
  } else {
  for (Entry e = header.next; e != header; e = e.next) {
  if (o.equals(e.element)) {
  remove(e);
  return true;
  }
  }
  }
  return false;
  }

  用过C的人应该感到很熟悉了,这里就是通过指针后移来查找相同的元素,注意这里最多只删除一个
  元素,符合List接口中的说明。
  
public boolean addAll(Collection c) {
  return addAll(size, c);
  }
  public boolean addAll(int index, Collection c) {
  int numNew = c.size();
  if (numNew==0)
  return false;
  modCount++;
  Entry successor = (index==size ? header : entry(index));
  Entry predecessor = successor.previous;
  Iterator it = c.iterator();
  for (int i=0; i
       Entry e = new Entry(it.next(), successor, predecessor);
  predecessor.next = e;
  predecessor = e;
  }
  successor.previous = predecessor;
  size += numNew;
  return true;
  }

  这里又看到熟悉的面孔了,在数据结构里面的链表中插入元素不就是这样吗?
  successor代表后一个指针,就是说插入的数据在他的前面,predecessor代表前一个指针,也就是要插入数据之前的指针。如果对数据结构比较了解的话,应该比较容易理解,这里我可以把代码改一下,但是不能算是优化:
 
 for (int i=0; i
       Entry e = new Entry(it.next(), null, predecessor);
  predecessor.next = e;
  predecessor = e;
  }
  predecessor.next = successor;  
  successor.previous = predecessor;

  这样修改和原来是一样的,如果Entry有一个这样的构造函数Entry(Object element,Entry previous)那就
  好了,那就可以用修改后的代码优化了(并没有多大的价值)。如果可以看明白为什么可以这样修改,那就完全理解了,如果对这种数据结构不熟悉的话,可以画一个链表,然后按代码执行修改你的链表,这个方法很有效的。
  
public void clear() {
  modCount++;
  header.next = header.previous = header;
  size = 0;
  }
  public Object get(int index) {
  return entry(index).element;
  } 
  public Object set(int index, Object element) {
  Entry e = entry(index);
  Object oldVal = e.element;
  e.element = element;
  return oldVal;
  }
  public void add(int index, Object element) {
  addBefore(element, (index==size ? header : entry(index)));
  }
  public Object remove(int index) {
  Entry e = entry(index);
  remove(e);
  return e.element;
  }
  private Entry entry(int index) {
  if (index < 0 || index >= size)
  throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
  Entry 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;
  }

  这个方法返回一个Entry,这里注意注意当index为0时返回的是head.next,不可能返回head。因为index>=0而且
   所以循环语句至少要执行一次。这里指针移动进行了优化,因为是一个双向链表,可以朝不同方向移动,size>>2相当于size=size/2
  
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;
  }

  这里唯一注意的就是index++的位置
  
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;
  }

  注意index--的位置
 
 public ListIterator listIterator(int index) {
  return new ListItr(index);
  }

  以下是一个私有内部类
  
private class ListItr implements ListIterator {
  private Entry lastReturned = header;
  private Entry next;
  //调用next()方法的节点
  private int nextIndex;
  private int expectedModCount = modCount;
  ListItr(int index) {
  if (index < 0 || index > size)
  throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
  if (index < (size >> 1)) {
  next = header.next;
  for (nextIndex=0; nextIndex
           next = next.next;
  } else {
  next = header;
  for (nextIndex=size; nextIndex>index; nextIndex--)
  next = next.previous;
  }
  }
  public boolean hasNext() {
  return nextIndex != size;
  }
  public Object next() {
  checkForComodification();
  if (nextIndex == size)
  throw new NoSuchElementException();
   lastReturned = next;
  next = next.next;
  nextIndex++;
  return lastReturned.element;
  }
  public boolean hasPrevious() {
  return nextIndex != 0;
  }
  public Object previous() {
  if (nextIndex == 0)
  throw new NoSuchElementException();
    lastReturned = next = next.previous;
  nextIndex--;
  checkForComodification();
  return lastReturned.element;
  }
  public int nextIndex() {
  return nextIndex;
  }
  public int previousIndex() {
  return nextIndex-1;
  }
  public void remove() {
  checkForComodification();
  try {
  LinkedList.this.remove(lastReturned);
  } catch (NoSuchElementException e) {
  throw new IllegalStateException();
  }
  if (next==lastReturned)  //这里表示删除的是调用previous()返回的元素。
  next = lastReturned.next; //next被删除,所以next要后移,索引不变。
  else
  nextIndex--;      //删除的是next.previous,所以索引要减1。      
  lastReturned = header;  //这里很重要:1.释放资源。2.不允许连续调用remove。
  expectedModCount++;
  }
  public void set(Object o) {
  if (lastReturned == header)
  throw new IllegalStateException();
  checkForComodification();
  lastReturned.element = o;
  }
  public void add(Object o) {
  checkForComodification();
  lastReturned = header;
  addBefore(o, next);
  nextIndex++;
  expectedModCount++;
  }
  final void checkForComodification() {
  if (modCount != expectedModCount)
  throw new ConcurrentModificationException();
  }
  }

  以下是Entry的定义:
  
private static class Entry {
  Object element;
  Entry next;
  Entry previous;
  Entry(Object element, Entry next, Entry previous) {
  this.element = element;
  this.next = next;
  this.previous = previous;
  }
  }

  很简单,就是一个双向链表的接点,只有一个构造函数而已,没有其他方法。这里的next和previous不就是一个引用吗?其实不就是一个C里面的指针吗!不过C语言不会处理空指针,直接让操作系统处理了,Java确实抛出一个系统异常NullPointerException,根本不给他破坏系统的机会。
  
private Entry addBefore(Object o, Entry e) {
  Entry newEntry = new Entry(o, e, e.previous);
  newEntry.previous.next = newEntry;
  newEntry.next.previous = newEntry;
  size++;
  modCount++;
  return newEntry;
  }
  private void remove(Entry e) {
  if (e == header)
  throw new NoSuchElementException();
   e.previous.next = e.next;
  e.next.previous = e.previous;
  size--;
  modCount++;
  }
  public Object clone() {
  LinkedList clone = null;
  try { 
  clone = (LinkedList)super.clone();
  } catch (CloneNotSupportedException e) { 
  throw new InternalError();
  }
  // Put clone into "virgin" state
  clone.header = new Entry(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 = header.next; e != header; e = e.next)
  clone.add(e.element);
   return clone;
  }
  public Object[] toArray() {
  Object[] result = new Object[size];
  int i = 0;
  for (Entry e = header.next; e != header; e = e.next)
  result[i++] = e.element;
  return result;
  }
  }
  public Object[] toArray(Object a[]) {
  if (a.length < size)
  a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
  int i = 0;
  for (Entry e = header.next; e != header; e = e.next)
  a[i++] = e.element;
   if (a.length > size)
  a[size] = null;
   return a;
  }
  private static final long serialVersionUID = 876323262645176354L;
  private synchronized 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 synchronized 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(null, null, null);
  header.next = header.previous = header;
  // Read in all elements in the proper order.
  for (int i=0; i
       add(s.readObject());
  }

  这里和ArrayList有一个区别就是size被声明为 transient的,因为这里调用的是add方法,这样
  size会自动增加,而在ArrayList是直接给数组赋值(效率更高)。
  这里比较一下ArrayList和LinkedList:
  1.ArrayList是基于数组,LinkedList基于链表实现。
  2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
  3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
  4.查找操作indexOf,lastIndexOf,contains等,两者差不多。
  这里只是理论上分析,事实上也不一定,比如ArrayList在末尾插入和删除数据就不设计到数据移动,不过还是有这么个建议:随机访问比较多的话一定要用ArrayList而不是LinkedList,如果需要频繁的插入和删除应该考虑用LinkedList来提高性能。
值得注意的是:当向ArrayList添加一个对象时,实际上就是将对象添加到了ArrayList底层维护的数组中;当向LinkedList添加一个对象时,实际上LinkedList会生成一个Entry对象,其结构为:
Entry
{
E element;
Entry<E> next;
Entry<E> previous;
}
其中element是我们向LinkedList中添加的元素,然后Entry又构造好了向前和向后的引用next和previous,最后将生成的Entry对象加入到链表中去。换句话说,LinkedList维护的是一个Entry对象
分享到:
评论

相关推荐

    Java 中Linkedlist类的源代码

    在Java编程语言中,LinkedList是一个实现List接口的类,它以双向链表的形式存储元素。...通过阅读源代码,开发者可以更好地理解Java集合框架的设计思想,以及在特定场景下如何选择合适的数据结构。

    LinkedList 部分源码

    ### LinkedList部分源码解析 #### 一、简介 `LinkedList`是Java集合框架的一个重要组成部分,它基于双向链表实现,既支持`List`接口也实现了`Deque`接口,因此可以作为列表、栈或者队列使用。双向链表的每个节点...

    Java中的ArrayList的底层源码解读、LinkedList、Vector的区别介绍

    阅读建议:结合代码进行阅读,在阅读的时候进行代码的比对,以及做笔记,结合自己不懂的地方反复观看,源码解读较为枯燥,文档中的步骤给的都很详细,需要大家结合源码一步一步的跟着步骤进行阅读,

    LinkedList源码

    LinkedList源码

    源码解析jdk7.0集合:LinkedList的底层实现原理.pdf

    接下来,我们将通过JDK 7.0的源码,深入探讨LinkedList的底层实现原理。 首先,LinkedList实现了List接口,因此它是一个有序列表。它还实现了Deque接口,这意味着LinkedList支持双端队列的操作。除了这些接口之外,...

    Map+List+ArrayList+LinkedList Java源码

    源码阅读可以揭示类的内部结构,包括数据成员、构造函数、方法实现等,从而帮助我们编写更高效、更健壮的代码。 **实际应用** 在实际开发中,选择使用`ArrayList`还是`LinkedList`取决于具体需求。如果频繁进行元素...

    JAVA利用LinkedList构造栈与队列

    这两个类(Stack和Queue)虽然基于LinkedList实现,但它们都提供了抽象数据类型的接口,隐藏了底层的实现细节,使得代码更具有封装性和可复用性。在实际项目中,如果对性能有较高要求,通常会使用专门的栈(如...

    LinkedList源码学习分析

    《LinkedList源码学习分析》 LinkedList作为Java集合框架中的一员,是基于链表数据结构实现的线程不安全容器。本文将深入探讨LinkedList的实现原理、核心方法的代码实现,并对比ArrayList,理解其特性和使用场景。 ...

    Java源码阅读的真实体会.pdf

    * 阅读 Struts 源代码前,建议看看 MvnForum 的源码,它部分实现了 Struts 的功能。 六、 阅读源码的经验总结 * 阅读源码需要技术基础、强烈的求知欲和耐心。 * 阅读源码可以帮助开发者更好地理解项目中部署和配置...

    linkedlist_链表_

    总结来说,"linkedlist_链表_"主题深入讲解了链表的基础概念,包括单链表和双链表的创建、节点的添加与删除、链表的逆序操作以及遍历技巧。掌握这些知识对于理解和应用数据结构,尤其是处理动态数据集合,具有重要...

    java LinkedList的添加删除操作

    在提供的JiHe6.java源代码中,可能包含了关于LinkedList添加删除操作的示例实现,通过阅读和理解代码,可以进一步加深对LinkedList的理解。而Java.jpg文件可能是与这个主题相关的辅助图表或类图,帮助视觉上理解...

    LinkedList源码分析_016.pdf

    《LinkedList源码解析》 LinkedList是Java集合框架中的一员,它是AbstractSequentialList的子类,同时也实现了List、Deque和Cloneable、Serializable接口。LinkedList作为双向链表,支持快速的添加、删除元素,尤其...

    Java源码:比较经典的一些Java源代码,适合于初学者

    Java源码是学习编程语言的重要资源,特别是对于初学者来说,通过阅读和分析源代码,可以深入理解语言的特性和编程技巧。这个压缩包包含了140个经典的Java源代码程序,涵盖了各种基础到进阶的编程概念。下面,我们将...

    ArrayList LinkedList Vector性能对比

    在Java编程语言中,集合框架是处理数据的重要组成部分。ArrayList、LinkedList和Vector是三种常见的动态数组实现,...在深入源码阅读和实践过程中,我们可以更深入地理解这些类的设计思想和实现方式,提升编程技能。

    第三章 LinkedList源码解析1

    LinkedList源码解析 LinkedList是Java中的一种链表实现,它的底层是一个环形双向链表。在 LinkedList 中,节点之间通过引用相互连接,形成一个链表结构。LinkedList 提供了多种方法来操作链表,包括添加、删除、...

    用LinkedList实现队列和栈

    本篇文章将探讨如何利用`LinkedList`来实现队列和栈这两种数据结构,以及其背后的原理和源码分析。 ### 1. 队列(Queue) 队列是一种先进先出(FIFO, First In First Out)的数据结构。在Java中,可以使用`...

    Java 实例 - 获取链表(LinkedList)的第一个和最后一个元素源代码-详细教程.zip

    本教程将深入探讨如何获取LinkedList中的第一个和最后一个元素,并提供相关的源代码实例。LinkedList类提供了丰富的API来操作链表,包括添加、删除、查找以及访问元素等。 1. **LinkedList简介** LinkedList是一个...

    ArrayList-LinkedList-源码.rar

    《ArrayList与LinkedList源码解析》 在Java编程中,ArrayList和LinkedList是两种常见的动态数组,它们都是Java集合框架的一部分,提供了对元素存储和操作的功能。本篇将深入探讨ArrayList和LinkedList的内部实现...

    Java集合系列之LinkedList源码分析

    Java集合系列之LinkedList源码分析 概述: 本文详细介绍了Java集合系列之LinkedList的源码分析,主要介绍了LinkedList的底层实现、成员变量、构造器、增删改查方法等。LinkedList是一种基于双向链表的数据结构,...

    JDK1.6中Arraylist,Vector,LinkedList源码

    2. 插入和删除:比较ArrayList、Vector和LinkedList在插入和删除元素时的代码实现,分析时间复杂度。 3. 线程安全:分析Vector如何实现线程安全,以及这对其性能的影响。 4. 链表结构:研究LinkedList的Node类,理解...

Global site tag (gtag.js) - Google Analytics