从源码分析java容器之LinkedList

引言

上一篇文章咱们一起分析了ArrayList,本篇咱们一起来看看它的姐妹LinkedList,分析的源码是JDK8版本。

1、LinkedList结构图

LinkedList继承了AbstractSequentialList,实现了List、Deque、Cloneable、Serializable接口,如下图所示:
在这里插入图片描述

  • AbstractSequentialList类:继承AbstractList,从该抽象类的英文注释中可以知道,该类提供了List接口的骨干实现,对“顺序访问”数据存储(如链接列表)支持。对于随机访问数据(如数组),应该优先使用 AbstractList。
  • Deque 接口:Deque定义了一个线性Collection,支持在两端插入和删除元素。
    现在,我们应该知道了LinkedList是一个双向链表结构,支持复制、序列化的。

2、分析源码

我们分析的顺序还是从对象的属性,构造方法,常用方法进行分析

2.1、属性

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;
	
	//...省略部分代码
}

上面源码中为LinkedList中的基本属性,其中size为LinkedList的长度,first为指向头结点,last指向尾结点,Node为LinkedList的一个私有内部类,其定义如下,即定义了item(元素),next(指向后一个元素的指针),prev(指向前一个元素的指针)。

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

我们假设LinkedList中的元素为[“A”,”B”,”C”],其内部的结构如下图所示:
在这里插入图片描述
从上图,我们可以清晰的看出LinkedList底层是双向链表的实现。

2.2、构造方法

public LinkedList() {}

public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
}

源码中只有两个构造方法,一个是空构造方法,啥事也没做,另外一个构造方法可以添加集合元素,转化为链表,我们可以看到方法是addAll,我们先看添加单个元素的add方法,addAll方法与其大同小异。

2.3、常用方法

2.3.1、新增
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.记录当前末尾节点,构造另外一个指向末尾节点的指针l
2.产生新的节点:在链表的末尾添加,next是为null的
3.last指向新的节点
4.这里有个判断,判断是否为第一个元素(当l==null时,表示链表中是没有节点的), 如果是第一节点,则使用first指向这个节点,若不是则当前节点的next指向新增的节点
5.size增加,操作记录modCount增加

2.3.2、删除
//方法1.删除指定索引上的节点
public E remove(int index) {
	//检查索引是否正确
	checkElementIndex(index);
	//这里分为两步,第一通过索引定位到节点,第二删除节点
	return unlink(node(index));
}
//方法2.删除指定值的节点
public boolean remove(Object o) {
	//判断是否为null,然后进行遍历删除
	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;
}

通过源码可以看出两个方法都是通过unlink()删除,咱们先看方法一中定位到节点的node(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的位置,是靠近first还是靠近last
  • 若靠近first则从头开始查询,否则从尾部开始查询,可以看出这样避免极端情况的发生,也更好的利用了LinkedList双向链表的特征

下面再分析删除节点中最核心的方法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;

	/删除的是第一个节点,first向后移动
	if (prev == null) {
		first = next;
	} else {
		prev.next = next;
		x.prev = null;
	}
	//删除的是最后一个节点,last向前移
	if (next == null) {
		last = prev;
	} else {
		next.prev = prev;
		x.next = null;
	}

	x.item = null;
	size--;
	modCount++;
	return element;
}
  • 1.获取到需要删除元素当前的值,指向它前一个节点的引用,以及指向它后一个节点的引用。
  • 2.判断删除的是否为第一个节点,若是则first向后移动,若不是则将当前节点的前一个节点next指向当前节点的后一个节点
  • 3.判断删除的是否为最后一个节点,若是则last向前移动,若不是则将当前节点的后一个节点的prev指向当前节点的前一个节点
  • 4.将当前节点的值置为null
  • 5.size减少并返回删除节点的值

上面就是LinkedList添加、删除元素的内部实现。

2.4、总结

上一篇我们分析了ArrayList,下面我们来总结对比下ArrayList和LinkedList的区别:

2.4.1、相同点
  • 1.接口实现:都实现了List接口,都是线性列表的实现
  • 2.线程安全:都是线程不安全的
2.4.2、区别
  • 1.底层实现:ArrayList内部是数组实现,而LinkedList内部实现是双向链表结构
  • 2.接口实现:ArrayList实现了RandomAccess可以支持随机元素访问,而LinkedList实现了Deque可以当做队列使用
  • 3.性能:新增、删除元素时ArrayList需要使用到拷贝原数组,而LinkedList只需移动指针,查找元素 ArrayList支持随机元素访问,而LinkedList只能一个节点接一个节点的去遍历

结束语

写源码分析文章是需要耗费巨大的精力的,如果本篇文章对你有帮助,请随手点个赞,谢谢!

原创文章 55 获赞 76 访问量 17万+

猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/105675395