Java基础五:LinkedList

概述

  • LinkedList是基于双向链表的非线性安全的一种线性的数据存储结构,由于这一特性,LinkedList还可以用作栈、队列或双端队列,LinkedList中的元素可以为空、可以重复同时又是有序的。
  • 既然是基于双向链表,那么每个元素都存储了指向前一个或者后一个元素的指针域,所以可以通过任意一个元素,通过向前或者向后寻址的方式去查找到前一个元素或者后一个元素。而且,头部和尾部中也通过指针域来相互关联。
  • LinkedList继承AbstractSequentialList(只能顺序访问,不能随机访问),实现了List、Deque(可以作为双端队列)、Cloneable(可以被克隆)、java.io.Serializable(支持序列化)。
    在这里插入图片描述

初始化

  • 无参构造函数
/**
  * Constructs an empty list.
  */
 public LinkedList() {
 }

LinkedList是不需要执行扩容的,初始化的时候直接创建了一个空的集合。

  • 带Collection对象的构造函数
/**
  * Constructs a list containing the elements of the specified
  * collection, in the order they are returned by the collection's
  * iterator.
  *
  * @param  c the collection whose elements are to be placed into this list
  * @throws NullPointerException if the specified collection is null
  */
 public LinkedList(Collection<? extends E> c) {
     this();
     addAll(c);
 }

首先调用无参构造方法创建一个空的集合对象,然后调用addAll方法处理传来的Collection对象。

添加元素

  • 直接添加元素,代码如下所示:
/**
  * Appends the specified element to the end of this list.
  *
  * <p>This method is equivalent to {@link #addLast}.
  *
  * @param e element to be appended to this list
  * @return {@code true} (as specified by {@link Collection#add})
  */
 public boolean add(E e) {
     linkLast(e);
     return true;
 }
 /**
   * Links e as last element.
   */
  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++;
  }

通过注释可以很清楚的看到,这个方法是把元素作为链表的最后一个元素。现在一步步来看:
首先把链表中的最后一个节点last赋给l,然后新建一个节点newNode,其中newNode的上一个节点是l,也就是链表的最后一个节点last,下一个节点是null,然后把新定义的节点newNode赋给最后一个节点last,作为链表的尾部;
最后的判断是区别这次操作是否是第一次给链表添加元素,也就是,如果添加之前,链表的尾部节点为空,那么证明这次是第一次添加,就把本次添加的节点作为链表的头节点,如果不是第一次,那么久把这次添加的节点作为原来链表的最后一个链表尾部节点last的下一个节点。

删除元素

  • 链表的删除元素如下所示:
/**
  * Removes the element at the specified position in this list.  Shifts any
  * subsequent elements to the left (subtracts one from their indices).
  * Returns the element that was removed from the list.
  *
  * @param index the index of the element to be removed
  * @return the element previously at the specified position
  * @throws IndexOutOfBoundsException {@inheritDoc}
  */
 public E remove(int index) {
     checkElementIndex(index);
     return unlink(node(index));
 }

checkElementIndex是一个检测下角标是否越界的,我们直接看unlink:

/**
  * 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;
     size--;
     modCount++;
     return element;
 }

这段代码总的来说,就是把当前要删除的节点X的上一个节点的下一个节点设置为X的下一个节点,X的下一个节点的上一个节点,设置为X的上一个节点,最后把X设置为null,等待被回收。

按照下角标添加元素

  • 代码如下:
/**
  * Inserts the specified element at the specified position in this list.
  * Shifts the element currently at that position (if any) and any
  * subsequent elements to the right (adds one to their indices).
  *
  * @param index index at which the specified element is to be inserted
  * @param element element to be inserted
  * @throws IndexOutOfBoundsException {@inheritDoc}
  */
 public void add(int index, E element) {
     checkPositionIndex(index);

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

第一步是检测下角标是否越界的,我们先不用管,接下来是判断:
如果要插入的元素位置和链表的长度相等,那么就执行linkLast方法,把新的元素添加到尾部,否则就调用linkBefore,把元素添加到该位置的节点的前面:

 /**
   * 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++;
  }

本方法是在非空节点之前插入一个元素,我们看下具体操作步骤:
首先定义一个节点pred,就是上一个节点;然后定义一个新节点newNode,新节点的上一个节点是pred,下一个节点是插入前链表的当前节点,最后把newNode作为当前节点的上一个节点,如果上一个节点是null,那么就把新定义的节点作为头结点,否则就作为原链表当前节点的下一个节点。这个地方有点绕,其实就是把要插入的节点,作为一个新的节点,插入到链表的非尾部位置,把链表的某一个位置打断,把新的节点放进去并重新拼接上。
``

查找元素

  • 根据下角标查找元素:
/**
  * Returns the element at the specified position in this list.
  *
  * @param index index of the element to return
  * @return the element at the specified position in this list
  * @throws IndexOutOfBoundsException {@inheritDoc}
  */
 public E get(int index) {
     checkElementIndex(index);
     return node(index).item;
 }

第一步依然是检测下角标是否越界的,不用管,直接看node(index).item:

/**
  * Returns the (non-null) Node at the specified element index.
 */
 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;
     }
 }

这是查找元素的代码,首先通过判断index和size/2的大小关系(size >> 1是位移运算,表示size/2,这种方式能提升代码运行效率),然后从index开始,分别向前后向后遍历查找元素(双向链表的特点)。

与ArrayList对比

通过分析源码,可以看出,LInkedList和ArrayList还是有很明显得区别的,本篇文章和上篇文章(ArrayList)都有涉及,主要是:
1、初始化
2、添加元素
3、删除元素
4、查找元素

总结

本文从源码出发,详细的介绍了LInkedList的特点:

  • 1、LinkedList的添加删除是非常快的
  • 2、LinkedList的查找需要遍历,大部分情况下性能不如ArrayList
  • 3、LinkedList每一个节点中存储着上一个节点的信息和下一个节点的信息,通过此方法把所有的节点联系起来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值