LinkedList源码分析

1.基本原理:LinkedList底层基于双向链表实现的,随机查找慢,需要从头开始一个一个比较,删除、插入快,只需要修改节点的引用就行了,没有扩容的说法

 

2.成员变量分析

 

/**
 * 元素数量
 */
transient int size = 0;

/**
 * 第一个元素,封装为一个Node
 */
transient Node<E> first;

/**
 * 最后一个元素,封装为一个Node
 */
transient Node<E> last;

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

 

4.双向链表数据结构:LinkedList里面有一个双向链表数据结构,每个节点就是一个Node,Node有一个prev指向上一个节点,next指向下一个节点,item存放实际元素值,first指向第一个Node,last指向最后一个Node,由下面图表示

 

5.boolean 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++;
}

画图分析

刚开始链表没有元素,first和last都指向null

第一次添加元素

final Node<E> l = last;

这一句以后多了一个l指针指向null

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

 

这一句构造了一个Node节点,prev指向l也为null,item为e,next为null,newNode 指向这个Node

last = newNode;

这一句以后last指向newNode指向的内容

if (l == null)

first = newNode;

else

l.next = newNode;

接着l==null成立,执行first = newNode,first 指向newNode指向的内容

方法执行完毕,局部变量指针去掉变成下面这样

现在集合有元素了,再次添加元素

final Node<E> l = last;

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

构造一个新的节点prev指向l指向的内容,item为e,next为null

last = newNode;

if (l == null)

first = newNode;

else

l.next = newNode;

l==null不成立了执行 l.next = newNode;

方法执行完毕,局部变量指针去掉变成下面这样

后面add方法以此类推新增一个节点

 

6.void add(int index, E element)源码分析

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

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

看看checkPositionIndex(index);

private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}

也就如果指定index不在0到size之间就抛出IndexOutOfBoundsException异常

 

if (index == size)

linkLast(element);

else

linkBefore(element, node(index));

 

如果index == size成立说明是往最后一个位置插入元素,调用 linkLast(element),和add()方法一样了,否则就是队列中间插入元素调用linkBefore(element, node(index))方法

先看看node(index)方法

/**
 * 返回指定位置的元素
 */
Node<E> node(int index) {
    // size >> 1相当于size÷2的意思,也就是说如果index小于size的一半,从头开始遍历
    // 如果index大于size的一半,从尾部开始倒着遍历
    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;
    }
}

再看看linkBefore(element, node(index))方法

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

假设链表中本来有3个元素,调用了add(1, "张三"),通过node(1)方法,找到了第一个位置的元素,然后调用了linkBefore("张三", succ)方法,刚开始数据结构如下图所示

接下来我们一行代码一行代码分析

final Node<E> pred = succ.prev;

多了个pred指针指向succ的prev指向的内容

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

创建了一个Node,perv指向pred指向的内容,item为指定的新元素,next为succ指向的内容

succ.prev = newNode;

succ的prev指向newNode指向的内容

if (pred == null)

first = newNode;

else

pred.next = newNode;

如果pred==null说明,succ的prev为null也就是index为0,第一个元素那么first就执行newNode了,否则执行pred.next = newNode;pred的next指向newNode指向的内容

方法执行完毕,取出局部变量后变为下面这样

指定位置插入元素操作就完成了

 

7.E set(int index, E element)源码分析

 

 

/**
 * 覆盖指定位置的元素,返回旧值
 */
public E set(int index, E element) {
    // 检查是否越界
    checkElementIndex(index);
    // 取出指定位置的元素
    Node<E> x = node(index);
    // item指定位置元素的item设置为新元素,return旧元素
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

 

8.public void addLast(E e)源码分析

 

 

/**
 * 插入到最后一个
 */
public void addLast(E e) {
    linkLast(e);
}

调用的linkLast(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++;
}

经过上面方法的分析,这个一看就明白,在最后位置插入一个元素

9.public void addFirst(E e)源码分析

 

/**
 * 插入到第一个
 */
public void addFirst(E e) {
    linkFirst(e);
}

private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

经过上面方法的分析,这个一看就明白,在第一个位置插入一个元素

 

10.E get(int index)源码分析

 

/**
 * 获取指定位置的元素
 */
public E get(int index) {
    // 校验index是否在0-size之间
    checkElementIndex(index);
    // 通过node方法判断index在小于size÷2就从头开始遍历找到元素,否则从尾部开始遍历找到元素
    return node(index).item;
}

11.E getFirst()源码分析

 

public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

直接返回first节点的item

 

12.E getLast()源码分析

 

public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

 直接返回last节点的item

 

 

13.E remove(int index)源码分析

 

 

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

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

 

先通过node(index)拿到要删除的元素,分析下unlink(node(index))方法

假设目前集合有4个元素,下图结构

调用了remove(1)方法,先通过node找到了item为张三的元素,调用unlink(node(index))

final E element = x.item;

element 指向x.item

final Node<E> next = x.next;

next指向x.next

final Node<E> prev = x.prev;

prev指向x.perv

if (prev == null) {

first = next;

} else {

prev.next = next;

x.prev = null;

}

如果prev == null说明要删除的是第一个元素让first指向next就行了,删除中间的元素执行

prev.next = next;

 x.prev = null;

 

 

if (next == null) {

last = prev;

} else {

next.prev = prev;

x.next = null;

}

如果next == null说明要删除最后一个元素,last指向prev,删除中间元素执行

next.prev = prev;

x.next = null;

x.item = null;

方法执行完毕局部变量去掉,没有引用的Node节点去掉变成下面这个样子

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LinkedList是Java中提供的一个双向链表实现类,其内部维护了一个first和last节点,分别表示链表的头和尾。以下是LinkedList源码分析: 1. 声明LinkedList类 ```java public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0; transient Node<E> first; transient Node<E> last; } ``` 2. 声明Node类 ```java 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; } } ``` 3. 实现LinkedList的方法 - add(E e)方法:将元素添加到链表末尾 ```java 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++; } ``` - add(int index, E element)方法:将元素插入到指定位置 ```java public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } void linkBefore(E e, Node<E> succ) { 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++; } ``` - remove(int index)方法:删除指定位置的元素 ```java public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } E unlink(Node<E> x) { 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--; return element; } ``` - get(int index)方法:获取指定位置的元素 ```java public E get(int index) { checkElementIndex(index); return node(index).item; } Node<E> node(int 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; } } ``` 以上就是LinkedList源码分析,通过对其源码分析,我们可以更深入地理解链表的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值