JDK17集合源代码阅读——LinkedList

LinkedList(双向链表)不仅实现了 List 接口,还实现了 Deque(双端队列)接口。

实现了List<E>, Deque<E>, Cloneable, java.io.Serializable等接口

    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;

主要维护了这样三个值(都不可以被序列化),first链表头指针,last链表尾指针,还有size

当然里面的节点类是一个双向节点,如下:

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;
        }
    }//内部的节点类——双向链表
    public LinkedList() {
    }
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

构造器不多只有两个,但是这个addAll方法需要注意(实现的是一种操作——把一条链表在某个地方剪开,把一段新的链条接进去,再把剩下的接好)

public boolean addAll(int index, Collection<? extends E> c) {//把一条链表在某个地方剪开,把一段新的链条接进去,再把剩下的接好
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;//寻找“断点”
        if (index == size) {// 如果在末尾插入
            succ = null; // 后面没节点了
            pred = last; // 前面就是当前的最后一个节点
        } else {// 如果在中间插入
            succ = node(index);// 找到当前索引处的节点
            pred = succ.prev;// 它的前驱就是断点的前半部分
        }

        for (Object o : a) {//遍历对象数组
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);//制作为节点加上去
            if (pred == null)// 如果 pred 是 null,说明是在头部插入
                first = newNode;
            else
                pred.next = newNode;// 否则,把前一个节点的 next 指向新节点
            pred = newNode;//移动指针:把 pred 指向刚刚插入的这个新节点
        }
				//重新连接断开的部分
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;// 新链条末尾的 next 指向原来的后继节点
            succ.prev = pred;// 原来后继节点的 prev 指向新链条的末尾
        }

        size += numNew;
        modCount++;
        return true;
    }

这一段看代码即可,逻辑非常清晰,其实和直接链表插入一个节点也没什么太大区别

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++;
    }//是直接在末尾添加的
    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }//获取队首元素,但不从链表中删除它
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }//出掉队首的元素
    public boolean offer(E e) {
        return add(e);
    }//直接加到队尾
    public void push(E e) {
        addFirst(e);
    }//入栈
    public E pop() {
        return removeFirst();
    }//出栈

这一部分就是一些常见的用法的源代码,其实就是链表操作,而且由于是双端链表甚至都不需要进行遍历就可以进行操作了

  • 作为 List(列表):add(末尾添加元素)、get(获取元素)
  • 作为 Queue(队列):offer(入队)、poll(出队)、peek(查看队首元素)
  • 作为 Deque(双端队列 / 栈):push(压栈)、pop(出栈)
  • 更通用的方法:removeFirst、removeLast、addFirst、addLast(所有的都是由这些构成的)

这些在我们用Java写代码题目的时候是很常见的,实在忘记了我们也可以使用最后一排的方法直接来操作,但是实际上,我们算法里面除了树图,其他的基本都是数组形式的多,但是在快速构建上,这些方法还是很方便的

List<String> list = new LinkedList<>();
list.add("A");
list.get(0);
Queue<String> queue = new LinkedList<>();
queue.offer("A"); // 入队
queue.poll();      // 出队
Deque<String> stack = new LinkedList<>();//作为栈的时候,对应Deque
stack.push("Top"); // 压栈
stack.pop();       // 弹栈

在现代 Java 开发中(尤其是 JDK 8 之后),ArrayList 通常是默认首选,LinkedList因为双端本来就浪费了很多资源,而且还是链表形式进行遍历非常麻烦,因此List里面主要还是使用ArrayList

而且LinkedList 同样不是线程安全的,LinkedList的实现在(JDK1.6 之前为循环链表,JDK1.7 取消了循环)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值