LinkedList源码分析

本文详细介绍了LinkedList的实现原理,包括其作为List和Deque接口的实现,以及如何通过Collections.synchronizedList()使其线程安全。LinkedList的成员变量、构造器、节点结构以及常用方法如add()、get()、remove()等的源码分析揭示了其实现细节。此外,文章还讨论了LinkedList的遍历方式,强调了迭代器的快速失败机制,并提醒避免在遍历时直接修改列表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LinkedList : List和Deque接口的双向链表实现。它也可以被当作堆栈、队列或双端队列进行操作。
作为平时常用的集合类,其实原理并没有十分复杂。
LinkedList是非线程安全的,通过Collections.synchronizedList()可以使其变为线程安全。
下面来看一下LinkedList的实现的接口和继承关系.

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

深入查看类关系:
在这里插入图片描述
值得注意的是它实现了Deque接口,Deque继承的是Queue接口。所以LinkedList比List还多了 offer(),peek(),poll()等方法。

先来看一下LinkedList的成员变量

	//元素个数
    transient int size = 0;

    //指向链表的第一个节点
    transient LinkedList.Node<E> first;

    //指向链表的最后一个节点
    transient LinkedList.Node<E> last;

构造器:LinkedList的构造器只有两个:空参构造器参数为集合的构造器

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

下面代码为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;
        }
    }

下面看一下常用方法的源码:
add () : 往List里添加元素默认在链表末尾添加调用linkLast()方法

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)
        	//如果LinkedList尾指针为空,即链表元素为0,将指向第一个元素的指针指向它
            first = newNode;
        else
            l.next = newNode;
        size++;
        //记录了链表改变了多少次,用于fail-fast
        modCount++;
    }

get(int index) : 折半查找

public E get(int index) {
        //检查了一下index是否超出有效范围
        checkElementIndex(index);
        //调用查找方法
        return node(index).item;
    }
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    //注意这里的查找是折半查找,即:先比较index和size,如果在前半部分则从头找起,如果在后半部分则从尾部找起
    LinkedList.Node<E> node(int index) {
        // assert isElementIndex(index);

        //在前半部分则从头找起
        if (index < (size >> 1)) {
            LinkedList.Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
            
        } else {//在后半部分则从尾部找起
            LinkedList.Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

在这里有个东西就必须说一下了:
LinkedList遍历:它有很多种遍历方式。这里要说明的是推荐使用迭代器遍历或ForEach
迭代器遍历:

		List<Integer> list=new LinkedList<>();
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            System.out.println(next);
        }

//简写:
	for(Iterator iter = list.iterator(); iter.hasNext();)
    	iter.next();

ForEach遍历:内部也是采用了Iterator的方式实现

for (Integer i:list) 

随机遍历千万不要

for (int i=0; i<list.size(); i++) {
    list.get(i);        
}

还有pollLast,pollLast
removeFirst,removeLast(破坏链表)等

列表迭代器是快速失败的,如果在创建送代器后的任何时间以任何方式修改列表结构,除了通过列表迭代器自己的 remove或ad方法,列表送代器将抛出
ConcurrentModificationException.

remove()

	public boolean remove(Object o) {
            //如果传入的o为null,而List是可以存储null的
            if (o == null) {
                //遍历链表查找node.item为null的节点并删除
                for (LinkedList.Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null) {
                        unlink(x);
                        return true;
                    }
                }
            } else {
                //遍历链表查找node.equals的节点并删除
                for (LinkedList.Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item)) {
                        unlink(x);
                        return true;
                    }
                }
            }
            return false;
        }

Fail-fast:
两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动,同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。线程A继续遍历执行next方法时,通告checkForComodification方法发现expectedModCount = N ,而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生fail-fast.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值