LinkedList源码分析

编程

LinkedList原理: 源码对应的jdk版本均为jdk11

先看LinkedList的构造方法:

有两个构造方法:1、无参数 2、参数为集合

//默认创建一个LinkedLise

List<Integer> link = new LinkedList<>();

//创建一个将其他类型集合的数据化为己用的LinkedList

List<Integer> link1 = new LinkedList<Integer>(new HashSet<Integer>());

看下LinkedList的属性:

    transient int size = 0;

/**

* Pointer to first node.

*/

transient Node<E> first;

/**

* Pointer to last node.

*/

transient Node<E> last;

LinkedList主要由若干个阶段链接而成,有两个终端节点,一个在起始位置,另一个在终点位置,并且还有一个属性size记录整个LinkedList的节点数。

接下来看下Node节点的结构:

    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;

}

}

可以看到Linked内部维护了一个双向链表。item为当前节点的值,next为指向下一个节点,prev变量指向前一个节点

LinkedList 无参构造函数:

    /**

* Constructs an empty list.

*/

public LinkedList() {

}

LinkedList无参构造函数什么操作都没有,因为其类的定义属性已经包含了LinkedList初始化时需要的一切。LinkedList的重点在于节点的构成和节点之间的操作

LinkedList的有参构造函数 -- 入参类型为集合类

    public LinkedList(Collection<? extends E> c) {

this();

addAll(c);

}

public boolean addAll(Collection<? extends E> c) {

return addAll(size, c);

}

public boolean addAll(int index, Collection<? extends E> c) {

checkPositionIndex(index);

Object[] a = c.toArray();

int numNew = a.length;

if (numNew == 0)

return false;

Node<E> pred, succ;

if (index == size) {

succ = null;

pred = last;

} else {

succ = node(index);

pred = succ.prev;

}

for (Object o : a) {

@SuppressWarnings("unchecked") E e = (E) o;

Node<E> newNode = new Node<>(pred, e, null);

if (pred == null)

first = newNode;

else

pred.next = newNode;

pred = newNode;

}

if (succ == null) {

last = pred;

} else {

pred.next = succ;

succ.prev = pred;

}

size += numNew;

modCount++;

return true;

}

我们主要分析下addAll()的源码部分,addAll(size, c)这部分;

1)我们调用LinkedList的有参构造函数,会调用addAll方法,addAll(size,c)

2)首先检查传入的index是否越界,把集合C转换为object[]

3)令pred指针移到链表的末尾,然后遍历Object[]数组,在链表末尾追加相应的元素

4)全部增加完成后将last指针移到链表末尾,linkedList的size增加相应的数组长度

然后分下我们常用的LinkedList中的方法

get()源码分析:

    public E get(int index) {

checkElementIndex(index);

return node(index).item;

}

1)检查传入的index,是否越界

2)根据node方法取出index对应的节点,并取出其值(item)

接下来我们看下node()方法:

    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;

}

}

1)首先根据index与size/2进行比较

2)如果index < size/2说明节点在LinkedList的前半段,从第一个节点遍历会更容易找

3)如果index >= size/2,那么从后往前找,这样会更加省时和容易找

所以如果这个节点越靠近中间,那么get()方法遍历的时间也越长,效率越低。而且随着LinkedList的节点数量越来越多,get()的执行性能也回迅速下降。所以在使用LinkedList时可以使用getFirst()、getLast()方法,直接调用类中的first和last变量

add(E e)源码分析:

插入一个元素,默认为在最后追加一个元素

public boolean add(E e) {

linkLast(e);

return true;

}

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

}

1)把我们传入的值,构造为一个Node对象,prev指针LinkedList的最后一个元素,next为空,然后吧LinkedList的last指针指向当前节点

2)判断LinkedList的最后一个Node是否为空,如果为为空的话,说明此时添加的为第一个节点,就是first指针指向当前元素

3)如果l最后一个元素不为空,就把之前的last指针的next指针指向当前节点。然后LinkdeList的size + 1

add(int index , E e)源码分析:

根据索引位置插入一个元素

    public void add(int index, E element) {

checkPositionIndex(index);

if (index == size)

linkLast(element);

else

linkBefore(element, node(index));

}

1)检查数组是否越界,如果index的值等于LinkedList的size值,则在LinkedList默认进行添加,使用linkLast()之前分析过

2)如果index的值小于LinkedList的size值,首先根据node()方法找到之前index上的元素,就是用linkBefore()进行添加

linkBefore():

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

}

1)找到对应index上的元素,然后根据传入的元素值构建一个新的Node,Node的前一个指针指向之前元素的prev,后一个指针指向之前元素,把之前元素的前一个指针指向新的Node

2)判断之前元素是否为第一个元素,如果为第一个元素,就把LinkedList的first节点指向新节点

3)如果不为第一个元素,之前元素的next指针指向新的Node。增加LinkedList的size +1

在ArrayList中插入数据可能有的数组扩容和数据元素移动造成的开销,在LinkedList都不需要,所以相比ArrayList,LinkedList的插入效率比较高。除了add()之外,比较常用的添加方法还有addFirst()和addLast()。

remove(Object o)源码分析:

LinkedList 移除一个对象

    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;

}

1)如果object对象为null的话,就遍历LinkedList,找到值为null的节点,并通过unlink()方法来删除这个节点

2)如果object对象不为null的话,使用equals方法来比较是否相等,如果相等通过unlink()方法删除这个节点

unlink() 源码分析:

    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;

}

1)如果unlink的元素前一个为null的话,说明它为第一个元素,则移动first指针到该元素的下一个。

2)如果不是第一个元素的话,在将该节点x的前节点的后指针指向x的后节点,把x的前节点置为null等待GC回收

3)如果X为最后一个节点的话,就把last的指针指向,x的前一个节点

4)如果不是最后一个元素的话,将x的后节点的前指针指向x的前节点,把x的后节点置为null等待GC回收

remove(int index):

    public E remove(int index) {

checkElementIndex(index);

return unlink(node(index));

}

1)检查数组是否越界,如果没有越界,通过node()方法找到对应索引位置的Node

2)根据unlink来移除该节点

以上是 LinkedList源码分析 的全部内容, 来源链接: utcz.com/z/515619.html

回到顶部