死磕Java容器—LinkedList

本文全面解析了LinkedList的内部结构,包括其双向链表的特性、构造方法、数据结构、添加、删除、修改、获取元素的方法,以及如何作为队列、栈和链表使用。

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

深入了解LinkedList。

环境:

  • eclipse 2019-03 (4.11.0)
  • jdk-12.0.1

eclipse中查看源码的方法:按住Ctrl键,鼠标左键单击代码(更详细请百度)。

容器:在Java中,“集合”有另外的用途,所以ArrayList、HashMap等皆称为容器类,其创建的一个对象就是一个容器。

简介

LinkedList是一个基于双向链表的List,除了能作为List使用之外,还可以用作队列或者栈。

LinkedList源码分析

1、class声明

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

源码分析:

LinkedList继承了抽象类AbstractSequentialList,AbstractSequentialList类声明

public abstract class AbstractSequentialList<E> extends AbstractList<E> {...}

LinkedList实现了接口List,目的为了Class类的getInterfaces这个方法可以直接返回List接口。

LinkedList实现了接口Deque,使LinkedList可以实现队列的相关功能,Deque类声明

public interface Deque<E> extends Queue<E> {...}

Queue源码

public interface Queue<E> extends Collection<E> {
    boolean add(E e);//向队列添加数据
    boolean offer(E e);//向队列添加数据
    E remove();//移除队首
    E poll();//移除队首
    E element();//获取队首元素
    E peek();//获取队首元素
}

LinkedList实现了接口Cloneable(标识接口),该接口不包含任何方法,实现它仅仅是为了指示可以使用Object类中的clone()方法来进行克隆。

LinkedList实现了Serializable接口(标识接口),表示该类可以进行序列化。该接口表示所有的非transient和非static字段都可以被序列化。如果要实现transient字段与static字段的序列化保存,必须声明writeObject和readObject方法

2、字段

transient int size = 0;

transient Node<E> first;

transient Node<E> last;

源码分析:

size:记录元素个数

first:第一个元素节点(链表首节点)

last:最后一个元素节点(链表尾节点)

3、构造方法

 public LinkedList() {
    }

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

源码分析:因为LinkedList数据结构是双向链表,不需要初始化大小,所以构造方法只有两个。

LinkedList():无参构造器。

LinkedList(Collection<? extends E> c):根据已有的Collection容器对象创建新的LinkedList对象

addAll()的目的是将一个Collection对象中的所有元素添加到当前容器中,来看看它的实现

public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);//调用addAll(int index, Collection<? extends E> c)
    }

public boolean addAll(int index, Collection<? extends E> c) {//在指定位置index及其之后插入Collection中的所有元素
        checkPositionIndex(index);//若index<0或index>size,抛出异常

        Object[] a = c.toArray();//先将Collection中的所有元素保存在一个数组中
        int numNew = a.length;
        if (numNew == 0)//若数组长度为0,即c中没有元素,返回false
            return false;

        Node<E> pred, succ;//pred指向前一个元素,succ指向当前元素
        if (index == size) {//确定当前元素succ的位置
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }

        for (Object o : a) {//往链表中循环添加数组a中的元素
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);//利用构造器封装数据
            if (pred == null)//若前一个元素为null,即index为0,数组中的第一个元素应该是链首
                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;
    }

4、数据结构

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存储的数据结构为静态内部类Node,双向链表。

5、添加元素add(),addFirst(),addLast()

public boolean add(E e) {//在链表末尾添加元素,添加成功返回true
        linkLast(e);
        return true;
    }
public void add(int index, E element) {//指定位置插入元素
        checkPositionIndex(index);//若index<0或index>size,抛出异常IndexOutOfBoundsException(outOfBoundsMsg(index))

        if (index == size)
            linkLast(element);//若要插入的位置刚好是链表尾部,调用linkLast()
        else
            linkBefore(element, node(index));//否则调用linkBefore()
    }

public void addFirst(E e) {//队列首部添加元素
        linkFirst(e);
    }
public void addLast(E e) {//队列尾部添加元素
        linkLast(e);
    }

public boolean offer(E e) {//队列尾部添加元素
        return add(e);
    }
public boolean offerFirst(E e) {//队列首部添加元素
        addFirst(e);
        return true;
    }
public boolean offerLast(E e) {//队列尾部添加元素
        addLast(e);
        return true;
    }

public void push(E e) {//压入元素
        addFirst(e);
    }

源码分析:

  • add(E e),offer(E e),offerFirst(E e),offerLast(E e)添加完元素后,返回true

源码中用到的linkLast(e)

void linkLast(E e) {//向链表尾部添加元素值e
        final Node<E> l = last;//首先获得链表最后一个元素的引用
        final Node<E> newNode = new Node<>(l, e, null);//利用数据结构的构造器将元素值e封装为一个元素节点
        last = newNode;//修改last为链表最新的结尾元素
        if (l == null)
            first = newNode;//若原来的链表是null的,则设置first为刚插入的元素newNode
        else
            l.next = newNode;//否则将刚封装好的节点添加到链表尾部
        size++;//元素计数加一
        modCount++;//结构性变化,计数加一
    }

源码中用到的linkBefore(element, node(index))

void linkBefore(E e, Node<E> succ) {//在元素succ的前面添加元素
        // assert succ != null;
        final Node<E> pred = succ.prev;//获得succ的前一个元素的引用
        final Node<E> newNode = new Node<>(pred, e, succ);//利用构造器将e封装为一个元素节点
        succ.prev = newNode;//修改succ的prev指向新的元素
        if (pred == null)
            first = newNode;//若succ是链表头的元素,修改first指向新的元素
        else
            pred.next = newNode;//否则修改前一个元素的next指向新元素
        size++;
        modCount++;
    }

源码中用到的node(index)

Node<E> node(int index) {//根据index获取链表中的元素
        // assert isElementIndex(index);

        if (index < (size >> 1)) {//若index小于元素个数的一半,则从链表首开始遍历元素
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//若index大于元素个数的一半,则从链表尾开始遍历元素
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

源码中用到的linkFirst(e)

private void linkFirst(E e) {//在链表首插入元素
        final Node<E> f = first;//首先获取第一个元素
        final Node<E> newNode = new Node<>(null, e, f);//利用构造器将元素值e封装为一个元素节点
        first = newNode;//修改first指向新的节点
        if (f == null)
            last = newNode;//若原本链表为null,设置last指向新的节点
        else
            f.prev = newNode;//否则修改原来的第一个节点的prev指向新的节点
        size++;
        modCount++;
    }

6、删除元素remove(),removeFirst(),removeLast(),poll(),pollFirst(),pollLast(),pop()

public E remove() {//移除链表第一个元素
        return removeFirst();
    }
public E remove(int index) {//移除指定位置index的元素
        checkElementIndex(index);//若index<0或index>=size,抛出异常IndexOutOfBoundsException(outOfBoundsMsg(index))
        return unlink(node(index));
    }
public boolean remove(Object o) {//移除指定元素
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {//这个循环还是有点意思的
                if (x.item == null) {
                    unlink(x);///调用unlink()删除节点
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
public E removeFirst() {//移除链表第一个节点
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
public E removeLast() {//移除链表最后一个节点
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

public E poll() {//移除队列头,并返回被移除的元素的值
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
public E pollFirst() {//移除队列头,并返回被移除的元素的值
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
public E pollLast() {//移除队列尾,并返回被移除的元素的值
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }

public E pop() {//弹出栈,并返回被弹出的元素的值
        return removeFirst();
    }

源码分析:

  • remove(Object o)返回boolean值
  • remove(int index)若index不符合要求,抛出异常
  • remove(),removeFirst(),removeLast(),pop()移除元素时,若指定的元素为null,抛出异常
  • poll(),pollFirst(),pollLast()移除元素时候,若指定的元素为null,返回null

源码中用到的unlink(node(index))

E unlink(Node<E> x) {//移除节点x,并返回被移除节点x的item值
        // assert x != null;
        final E element = x.item;//首先获得要删除节点x的item,next以及prev
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {//若prev为null,表示节点x是链表的第一个节点,直接修改first
            first = next;
        } else {//否则设置节点x的上一个节点的next指向节点x的下一个节点,并将节点x的prev修改为null,方便GC
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {//若next为null,表示节点x是链表的最后一个节点,直接修改last
            last = prev;
        } else {//否则设置节点x的下一个节点的prev指向节点x的上一个节点,并将节点x的next修改为null,方便GC
            next.prev = prev;
            x.next = null;
        }

        x.item = null;//最后将节点的item修改为null,方便GC
        size--;
        modCount++;
        return element;
    }

源码中用的的unlinkFirst(f)

private E unlinkFirst(Node<E> f) {//链表中移除首节点,返回被移除的节点值
        // assert f == first && f != null;
        final E element = f.item;//首先获取首节点的item和next
        final Node<E> next = f.next;
        f.item = null;//将item与next置空
        f.next = null; // help GC
        first = next;//修改first指向原首节点的下一个节点
        if (next == null)//如果原首节点的下一个节点为null,表示链表中只有一个节点,修改last为null
            last = null;
        else
            next.prev = null;//否则修改下一个节点的prev为null
        size--;
        modCount++;
        return element;
    }

源码中用到的unlinkLast(l)

private E unlinkLast(Node<E> l) {//链表中移除尾节点
        // assert l == last && l != null;
        final E element = l.item;//首先获取尾节点的item和prev
        final Node<E> prev = l.prev;
        l.item = null;//将尾节点的item和prev置空
        l.prev = null; // help GC
        last = prev;//修改last为原尾节点的前一个节点
        if (prev == null)//若前一个节点为null,表示链表中只有一个节点,将first修改为null
            first = null;
        else
            prev.next = null;//否则将前一节点的next修改为null
        size--;
        modCount++;
        return element;
    }

7、修改元素set()

public E set(int index, E element) {//将指定位置index的元素值修改为element,返回原数据
        checkElementIndex(index);//若index<0或index>=size,抛出异常IndexOutOfBoundsException(outOfBoundsMsg(index))
        Node<E> x = node(index);//调用node(index)获得指定位置index的节点元素
        E oldVal = x.item;//先获取节点原来的item值
        x.item = element;//修改节点值
        return oldVal;//返回原值
    }

8、获取元素get(),getFirst(),getLast(),peek(),peekFirst(),peekLast(),element()

public E get(int index) {//获取指定位置index的元素值
        checkElementIndex(index);//若index<0或index>=size,抛出异常IndexOutOfBoundsException(outOfBoundsMsg(index))
        return node(index).item;//先调用node(index)获得目标节点,再获得item
    }
public E getFirst() {//获取链表第一个节点的值
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
public E getLast() {//获取链表的最后一个节点的值
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

public E peek() {//获得队头元素的值
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }
public E peekFirst() {//获得队头元素的值,和peek()完全一样
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }
public E peekLast() {//获得队尾元素的值
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }
public E element() {//获得链表的第一个节点的值
        return getFirst();
    }

源码分析:

  • get(int index),peek(),peekFirst(),peekLast()获取元素时,若目标为空,返回null
  • getFirst(),getLast(),element()获取元素时,若目标为空,抛出异常

9、遍历元素iterator()

public ListIterator<E> listIterator(int index) {//返回一个迭代器对象
        checkPositionIndex(index);//若index<0或index>size,抛出异常IndexOutOfBoundsException(outOfBoundsMsg(index))
        return new ListItr(index);
    }

private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned;//最后一个被返回的元素
        private Node<E> next;//下一个元素
        private int nextIndex;//下一个元素的索引
        private int expectedModCount = modCount;

        ListItr(int index) {//ListItr的构造方法,传入的index决定接下来遍历元素的开始位置
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);//修改next指向index位置的节点
            nextIndex = index;//保存节点位置index
        }

        public boolean hasNext() {//判断是否还有下一个元素
            return nextIndex < size;
        }

        public E next() {//获取下一个元素的值,在调用next()之前,需要调用hasNext()判断下一个元素是否存在
            checkForComodification();//检查modCount是否改变,即容器是否发生了结构性变化
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;//lastReturned指向next指向的元素
            next = next.next;//next指向下一个元素
            nextIndex++;
            return lastReturned.item;//返回元素值item
        }

        public boolean hasPrevious() {//判断是否还有前一个元素
            return nextIndex > 0;
        }

        public E previous() {//获取前一个元素的值
            checkForComodification();//检查modCount是否改变,即容器是否发生了结构性变化
            if (!hasPrevious())
                throw new NoSuchElementException();

            lastReturned = next = (next == null) ? last : next.prev;
            nextIndex--;
            return lastReturned.item;
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex - 1;
        }

        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(lastReturned);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }

        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.item = e;
        }

        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (modCount == expectedModCount && nextIndex < size) {
                action.accept(next.item);
                lastReturned = next;
                next = next.next;
                nextIndex++;
            }
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

源码分析:因为LinkedList是一个双向链表,所以它的迭代类与ArrayList有些不同。单独来看ListItr类,我们最常用的就是其中的hasNext(),next(),hasPrevious(),previous()四个方法

10、链表转数组toArray()

public Object[] toArray() {//返回一个包含所有元素的值的数组
        Object[] result = new Object[size];
        int i = 0;
        for (Node<E> x = first; x != null; x = x.next)//往数组中循环添元素值
            result[i++] = x.item;
        return result;
    }

实例

1、LinkedList作为栈的实现

	public static void main(String[] args) {
		LinkedList<String> stack=new LinkedList<String>();
		stack.push("abc");//压栈
		stack.peek();//获取最后一个压入栈的元素
		stack.pop();//弹出最后一个压入栈的元素
	}

2、LinkedList作为单向队列的实现

	public static void main(String[] args) {
		Queue<String> queue=new LinkedList<String>();
		queue.offer("abc");//队尾添加元素
		queue.peek();//队头获取元素
		queue.poll();//队头删除元素
	}

3、LinkedList作为双向队列的实现

	public static void main(String[] args) {
		Deque<String> deque=new LinkedList<String>();
		deque.addFirst("abc");//队头添加元素
		deque.addLast("bcd");//队尾添加元素
		deque.getFirst();//队头获取元素
		deque.getLast();//队尾获取元素
		deque.removeFirst();//队头删除元素
		deque.removeLast();//队尾删除元素
	}

4、如果仅仅只是希望将LinkedList作为一个链表来使用

	public static void main(String[] args) {
		LinkedList<String> list=new LinkedList<String>();
		list.addFirst("abc");//链首添加
		list.addLast("bcd");//链尾添加
		list.add(0, "aaa");//指定插入
		
		list.getFirst();//链首获取
		list.getLast();//链尾获取
		list.get(0);//指定获取

		list.removeFirst();//链首删除
		list.removeLast();//链尾删除
		list.remove(0);//指定删除
		list.remove("bcd");//指定删除
		
		list.set(0, "bbb");//修改
	}

总结

  1. LinkedList是一个基于双向链表的List,且还是一个双端队列,具有队列,栈的特性
  2. LinkedList可以根据“索引”来 添加/获取/删除/修改 元素,但这种做法效率比较低

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值