LinkedList源码分析

本文详细介绍了LinkedList的内部结构,包括其实现的多个接口如List、Deque、Queue等,构造方法及主要方法如add、remove等的实现原理。此外还概述了其相对于ArrayList的优势与不足。

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

LinkedList简介

LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。

LinkedList 实现 list接口,具有集合的添加删除功能。

LinkedList 实现 Queue 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。

LinkedList相对于ArrayList来说,是可以快速添加,删除元素,但是访问数据相对于ArrayList会比较慢

LinkedList属性

public class LinkedList<E> extends AbstractSequentialList<E> implements
        List<E>, Deque<E>, Queue<E>, Cloneable, Serializable {

    private static final long serialVersionUID = 876323262645176354L;

	//链表大小
    transient int size = 0;
	//头节点
    transient Link<E> voidLink;

LinkedList内部类

//节点类
private static final class Link<ET> {
				//数据
        ET data;
				//该节点的前后节点引用
        Link<ET> previous, next;

        Link(ET o, Link<ET> p, Link<ET> n) {
            data = o;
            previous = p;
            next = n;
        }
    }

LinkedList构造方法

public LinkedList() {
        voidLink = new Link<E>(null, null, null);
        voidLink.previous = voidLink;
        voidLink.next = voidLink;
    }

该构造方法创建一个空的队列

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

该构造方法先创建一个空的链表,并将构造方法里传进来的集合数据全部添加到链表当中

LinkedList方法

 @Override
    public boolean addAll(Collection<? extends E> collection) {
    //获取集合大小
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;
				//获取当前的节点的前驱节点
        Link<E> previous = voidLink.previous;
        //遍历集合数据
        for (E e : elements) {
        //根据数据创建新的数据节点
            Link<E> newLink = new Link<E>(e, previous, null);
            //设置前驱节点的下一节点为当前的新节点
            previous.next = newLink;
             //设置前驱节指向当前的新节点
            previous = newLink;
        }
        //遍历完成之后当前的前驱几点为链表最后一个节点的前驱节点
        previous.next = voidLink;
        voidLink.previous = previous;
        size += adding;
        modCount++;
        return true;
    }

该方法将传入的集合中的数据添加到当前链表中

@Override
    public void add(int location, E object) {
    //判断节点插入的位置是链表长度范围之内
        if (location >= 0 && location <= size) {
            Link<E> link = voidLink;
             //节点插入位置如果小于链表大小的一半则从链表的开始位置遍历下一节点
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
               //节点插入位置如果大于或等于链表大小的一半则从链表的末尾位置遍历前一节点
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            //找到location位置的节点,并与前一节点断开连接,将新节点插入两个节点之间
            Link<E> previous = link.previous;
            Link<E> newLink = new Link<E>(object, previous, link);
            previous.next = newLink;
            link.previous = newLink;
            size++;
            modCount++;
        } else {
            throw new IndexOutOfBoundsException();
        }
    }
 public void add(int location, E object)  方法 在location 位置添加一个节点,数据object

@Override
    public boolean add(E object) {
        return addLastImpl(object);
    }

    private boolean addLastImpl(E object) {
        Link<E> oldLast = voidLink.previous;
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
        voidLink.previous = newLink;
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
    }

add(E object) 方法和 addLastImpl(E object) 在链表尾部插入新的数据节点


 @Override
    public boolean addAll(int location, Collection<? extends E> collection) {
        if (location < 0 || location > size) {
            throw new IndexOutOfBoundsException();
        }
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;

        Link<E> previous = voidLink;
        if (location < (size / 2)) {
            for (int i = 0; i < location; i++) {
                previous = previous.next;
            }
        } else {
            for (int i = size; i >= location; i--) {
                previous = previous.previous;
            }
        }
        Link<E> next = previous.next;
        for (E e : elements) {
            Link<E> newLink = new Link<E>(e, previous, null);
            previous.next = newLink;
            previous = newLink;
        }
        previous.next = next;
        next.previous = previous;
        size += adding;
        modCount++;
        return true;
    }

该方法用来在链表某一位置插入一个集合 与 public boolean addAll(Collection<? extends E> collection) 方法相比增加了一个查找链表节点位置的流程,具体原理是判断当前节点的位置是在前半部分还是在后半部分,从而只需要遍历一半,提高运行效率。

public void addFirst(E object) {
        addFirstImpl(object);
    }

    private boolean addFirstImpl(E object) {
        Link<E> oldFirst = voidLink.next;
        Link<E> newLink = new Link<E>(object, voidLink, oldFirst);
        voidLink.next = newLink;
        oldFirst.previous = newLink;
        size++;
        modCount++;
        return true;
    }

以上两个方法用于在链表头添加节点

 @Override
    public void clear() {
        if (size > 0) {
            size = 0;
            voidLink.next = voidLink;
            voidLink.previous = voidLink;
            modCount++;
        }
    }

该方法用于清空链表,只需要改变链表当前节点的指向,全部指向自己

@Override
    public boolean contains(Object object) {
        Link<E> link = voidLink.next;
        if (object != null) {
            while (link != voidLink) {
                if (object.equals(link.data)) {
                    return true;
                }
                link = link.next;
            }
        } else {
            while (link != voidLink) {
                if (link.data == null) {
                    return true;
                }
                link = link.next;
            }
        }
        return false;
    }

该方法用于判断链表中是否包含某一数据,方法时是遍历整个链表,直到找到该节点返回true或者遍历到头节点返回false

@Override
    public E get(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

该方法用于从头节点或尾节点开始遍历获取某一位置的节点的数据

 public E removeFirst() {
        return removeFirstImpl();
    }

    private E removeFirstImpl() {
        Link<E> first = voidLink.next;
        if (first != voidLink) {
            Link<E> next = first.next;
            voidLink.next = next;
            next.previous = voidLink;
            size--;
            modCount++;
            return first.data;
        }
        throw new NoSuchElementException();
    }

该方法用于从头节点删除一个节点

@Override
    public E remove(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link<E> previous = link.previous;
            Link<E> next = link.next;
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

该方法用于删除某一位置的节点,通过遍历找到该节点并将该节点的前驱节点指向该节点的后继节点,返回该节点的值


LinkedList其他方法

 LinkedList还提供了其他具有栈和队列特点的方法比如 pop(), push(E e) ,poll(),peek() 等方法都比较简单




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值