List源码解析之LinkedList 源码分析

LinkedList简介

实现了List和Deque接口,既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)(处理栈和队列问题,首选ArrayDeque,它的性能比LinkedList作栈和队列使用好很多)。
LinkedList是一种双向链表,通过firstlast引用分别指向链表的第一个和最后一个元素。
LinkedList是非线程安全的,也就是说它不是同步的,适合单线程环境使用;需要多个线程并发访问,可以先采用Collections.synchronizedList()方法对其进行包装。
LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆。

属性和构造函数

transient int size = 0;    //数量
transient Node<E> first;   //首节点
transient Node<E> last;    //尾节点

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;
  }
}

/**
 * Constructs an empty list.
 */
public LinkedList() {
}

/**
 * 构造一个包含指定collection 的元素的列表,这些元素按照
 * 该collection 的迭代器返回它们的顺序排列的
 */
public LinkedList(Collection<? extends E> c) {
  this();
  addAll(c);
}

存储

set(int index, E element)

// 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素
public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

add(E e)

// 添加指定元素到链表尾部,花费时间为常数时间
public boolean add(E e) {
  linkLast(e);
  return true;
}

/**
 * Links e as last element.
 链接e作为最后一个元素,默认向表尾节点加入新的元素
 */
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++;
}

add(int index, E element)

// 在指定位置插入
public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

// 下标位置越界检查
private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
      throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

  /**
   * Inserts element e before non-null Node succ.
   */
  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++;
  }

addAll(Collection<? extends E> c)

// 按照指定collection 的迭代器所返回的元素顺序,将该collection 中的所有元素添加到此列表的尾部
public boolean addAll(Collection<? extends E> c) {
  return addAll(size, c);
}

// 具体看源码吧

读取

get(int index)

// 返回指定下标的元素
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

删除

1.先找到要删除元素的引用,2.修改相关引用,完成删除操作

remove(int index)

使用下标计数

// 删除指定位置的元素,并返回
public E remove(int index) {
  checkElementIndex(index); // 检查下标是否越界
  return unlink(node(index));
}

    /**
     * Unlinks non-null node x.
     */
    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; //let GC work
        size--;
        modCount++;
        return element;
    }

remove(Object o)

使用equales方法

// 删除指定元素
public boolean remove(Object o) {
  if (o == null) {
    for (Node<E> x = first; x != null; x = x.next) {
      if (x.item == null) {
        unlink(x);
        return true;
      }
    }
  } else {
    for (Node<E> x = first; x != null; x = x.next) {
      if (o.equals(x.item)) {
        unlink(x);
        return true;
      }
    }
  }
  return false;
}

增删元素的时候,只需改变指针,不需要像数组那样对整体数据进行移动、复制等消耗性能的操作。

遍历

遍历的时候耗时,for循环(无穷大) > ForEach > 迭代器,优先使用迭代器方式。

```java
for(Iterator it = list.iterator();it.hasNext();){}

```

队列操作

E peek()

// 获取第一个元素
public E peek() {
  final Node<E> f = first;
  return (f == null) ? null : f.item;
}

E peekFirst()

// 获取第一个元素,同E peek()
public E peekFirst() {
  final Node<E> f = first;
  return (f == null) ? null : f.item;
}

E peekLast()

// 获取最后一个元素
public E peekLast() {
  final Node<E> l = last;
  return (l == null) ? null : l.item;
}

E pop()

// 移除第一个元素并返回
public E pop() {
  return removeFirst();
}

E pollFirst()

// 移除第一个元素,同E pop()
public E pollFirst() {
  final Node<E> f = first;
  return (f == null) ? null : unlinkFirst(f);
}

E pollLast()

// 移除最后一个元素
public E pollLast() {
  final Node<E> l = last;
  return (l == null) ? null : unlinkLast(l);
}

push(E e)

// 队首添加一个元素
public void push(E e) {
  addFirst(e);
}

总结

  1. 基于双向循环链表实现,不存在容量不足的问题,没有扩容的方法
  2. 增删元素快,查找慢
  3. 元素排列有序,可重复,可为null
  4. 实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。
  5. 非同步,线程不安全

参考

======华丽丽的分隔线======
作者:jimzhang
出处:http://www.jianshu.com/p/9d223d28eadb
版权所有,欢迎保留原文链接进行转载:)

时间: 2024-08-29 19:20:22

List源码解析之LinkedList 源码分析的相关文章

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全的,只在单线程下适合使用. LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆. Linked

Spring 源码解析之ViewResolver源码解析(四)

Spring 源码解析之ViewResolver源码解析(四) 1 ViewResolver类功能解析 1.1 ViewResolver Interface to be implemented by objects that can resolve views by name. View state doesn't change during the running of the application, so implementations are free to cache views. I

Spring 源码解析之DispatcherServlet源码解析(五)

Spring 源码解析之DispatcherServlet源码解析(五) 前言 本文需要有前四篇文章的基础,才能够清晰易懂,有兴趣可以先看看详细的流程,这篇文章可以说是第一篇文章,也可以说是前四篇文章的的汇总,Spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一个Servlet来进行转发的,同时解释了为什么需要在web.xml进行如下配置,因为Spr

【Java源码解析】-- HashMap源码解析

目录 源码解析 1.构造方法 无参构造方法 int型参数的构造方法 int,float两个参数的构造方法 hsah方法 2.添加元素(put()方法) 3.扩容方法(resize()方法) 4.获取元素(get()方法) 5.移除元素(remove()) 6.树化(treeifyBin()) 关于HashMap常见的问题 1.为什么容量始终是2的幂次? 3.既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于等于8个的时候才转换红黑树? 4.JDK1.7 扩容死锁产生原因 5.JDK

转:【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使

JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)

文章系作者原创,如有转载请注明出处,如有雷同,那就雷同吧~(who care!) 一.写在前面 这是源码分析计划的第一篇,博主准备把一些常用的集合源码过一遍,比如:ArrayList.HashMap及其对应的线程安全实现,此文章作为自己相关学习的一个小结,记录学习成果的同时,也希望对有缘的朋友提供些许帮助. 当然,能力所限,难免有纰漏,希望发现的朋友能够予以指出,不胜感激,以免误导了大家! 二.稳扎稳打过源码 首先,是源码内部的成员变量定义以及构造方法: 1 /** 2 * Default in

[java源码解析]对HashMap源码的分析(二)

上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得"拉链法". 我打算按这个顺序来讲HashMap:几个关键属性 -> 构造方法-> 存取元素方法 ->解决hash冲突方法->HashMap扩容问题. 4个关键属性: /** *HashMap的存储大小 */ transient int size; /** * HashMa

AQS源码解析(一)-AtomicBoolean源码解析

基本类: AtomicInteger AtomicLong AtomicBoolean 数组类型: AtomicIntegerArray AtomicLongArray AtomicReferenceArray 介绍 由于在多线程条件下,如果对共享变量修改容易造成数据不一致的情况,所以对于共享变量需要保证线程安全有有如下几种方式: 使用lock或者synchronized进行同步共享变量 使用CAS方法来保证修改变量为原子性操作 该类为后者,基于CAS方式修改具有原子性. 实现原理 将boole