LinkedList方法解析及实现原理

LinkedList方法解析及实现原理

LinkedList为一个链表类型的List列表,列表在频繁的插入和删除列表元素的时候,使用LinkedList比使用ArrayList将更为高效。

目录

LinkedList的构造函数很简单,没有什么额外说明的必要。但是LinkedList中的一些参数可能说明一下会更好。
在引入参数之前,我们需要先简单介绍一下LinkedList的内部静态类Node结点。Node的源码不多,所以我想直接贴出来看下。

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对象中包含了两个Node对象,一个是前结点prev,一个是后结点next,同时还有结点的值item
LinkedList中维护了两个结点Node,首结点first,尾结点last。好了,介绍到这里我们差不多就可以开始我们LinkedList的一些方法讲解了。


add(int index, E element)

checkPositionIndex(index); //边界检测

if(index == size)
  linkLast(element); //链表尾部插入
else
  linkBefore(element, node(index));  // 索引index前插入

此添加元素方法首先会先调用checkPositionIndex(index)进行边界检测。对index的值进行>=0 && <=size的边界检测。如果index在合理范围内,那么就会开始插入操作。但在插入操作的时候会进行一次所添加的index是否为size,即是否想把元素值插入到列表的最后一项。因为其实大部分操作都是从尾部开始加值。如果加入最后一项,那么调用包内方法linkLast(E e)

final Node<E> I = last;
final Node<E> newNode = new Node<>(I, e, null);  // 新建一个结点,该结点的前部指向LinkedList的尾部结点last
last = newNode;  // 将新的结点声明为链表的尾部,即将newNode赋值给last
if(I == null)
  first = newNode;   // 如果链表没有结点,那么就将newNode作为链表尾部的同时也作为链表的首部
else
  I.next = newNode;  // 如果链表有结点,在链表尾部加入新结点。
size++;

那么当index不为size的时候,即不是从尾部插入的时候,那么就需要在某一项前插入,可以调用linkBefore(E e, Node<E> succ)。讲解这个方法前,我想先讲解下上述源码中node(int index)这个函数。
初看这个函数可能很简单,通过索引index来查到链表中的结点。确实是这样的,但是内部的实现方法呢,之前我们在ArrayList中是通过正序遍历,但是这里就不是了。因为ArrayList实质是一个数组,数组的索引是很快的。而我们这里的LinkedList却是一个一个的结点。所以只能依次遍历过去,但如果从首部开始一个个遍历的话效率又太低,但是我们又无法通过数组那样直接通过索引找到,只能通过一个节点的下一个节点的这样寻找。所以源码中采用了一种二分查找的方式。node(int index)先判断index是否在链表的前半部分size >> 1,如果是的话那就正序开始遍历。否则在链表后半部分的话,那就从尾部结点开始逆序遍历。

好了,我们了解了链表中通过index如何查找到结点,那现在就可以开始介绍linkBefore(E e, Node<E> succ)了。先贴源码如下:

final Node<E> pred = succ.prev;  // succ为链表中处在index处的结点。
final Node<E> newNode = new Node<>(pred, e, succ);  //新建一个元素为e的结点,其头部指向succ的前一个结点,尾部指向succ.
succ.prev = newNode;   //插入结点,将新节点指向index处结点的头部
if(pred == null)
  first = newNode;    // 如果succ之前没有结点,那么把newNode插入到succ之前,那么newNode自然而然成为了链表的头结点了
else
  pred.next = newNode;  //否则应该将newNode查到前一个结点pred的尾部。
size++;

该方法总体思路就是,将元素E element插入的时候,新建一个element的结点,然后将处于index的结点和之前的结点断开。然后再将新建结点收尾依次对应插入进去。


add(E e)

该方法其实就是在链表的尾部加入元素E,也即调用上述所说的linkLast(E element)方法。


get(int index)

从链表中根据索引index来获取元素,其实实现原理我们之前在add(int index, E element)也有说过,通过index调用node(int index)拿到处于索引index处的结点。然后再返回该结点的item即可。


remove(int index)

链表移除index处的元素,先通过node(int index)找到位于index处的结点,然后通过unlink(Node<E> e)来解除该结点的绑定。

final E element = x.item;
final Node<E> next = x.next;
final Node<e> prev = x.prev;

if(prev == null){
  first = next;  //如果前结点为空的话,那么next结点就是第一个结点了,所以赋值给first。
}else{
  prev.next = next;  //否则prev的尾部指向next结点
  x.prev = null;    // x结点的首部不再指向prev,所以赋值null,有助GC。
}

if(next == null){
  last = prev;  // 如果x之后指向的结点next为空的话,那么说明x后面没有结点了,此时又移除x结点,所以链表尾部结点就是prev结点了。
}else{
  next.prev = prev;  //否则next的首部应该指向prev,之前是指向x,但因移除x,所以现在指向prev
  x.next = null;
}

x.item = null;  // item置null,有助GC。
size--;
modCount++;
return element;

remove(Object o)

该方法传入参数是Object o不是索引index了。所以无法通过之前的二分的方法去一个个遍历到Objdec o,因此只能通过正序遍历在LinkedList中一个个找到元素Object o。并调用unlink(Node<E> x)将其移除。贴下源码让大家看下。

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;

小结: LinkedList维护了一个链表结构,适合插入和删除元素,查找的效率低。和ArrayList一样,LinkedList同样是非线程安全。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值