集合框架----源码解读LinkedList篇

1.LinkedList官方介绍

列表和 Deque 接口的双向链表实现。实现所有可选的列表操作,并允许所有元素(包括 null)。
所有操作都按照双向链表的预期执行。索引到列表中的操作将从列表的开头或结尾遍历列表,以更接近指定索引为准。

请注意,此实现不同步。如果多个线程同时访问链表,并且至少有一个线程在结构上修改了该列表,则必须在外部同步该列表。(结构修改是添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。这通常是通过在自然封装列表的某个对象上进行同步来实现的。如果不存在此类对象,则应使用 Collections.syncdList 方法“包装”列表。最好在创建时执行此操作,以防止意外地对列表进行不同步访问:

List list = Collections.synchronizedList(new LinkedList(...));

此类的迭代器和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,则除了通过迭代器自己的 remove 或 add 方法之外,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。
请注意,无法保证迭代器的快速故障行为,因为一般来说,在存在不同步的并发修改的情况下,不可能做出任何硬保证。Fail-fast 迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖于此异常的正确性的程序是错误的:迭代器的快速故障行为应仅用于检测错误。

该类是Java集合框架的成员。

 2.LinkedLis的add方法

debug启动

一直下一步,进入node方法 自己是2,他的上一个元素是1,下一个元素没有存,所以是null,

 之前最后一个元素是1,现在新的最后一个元素是2

 指明1的下一个节点就是2,也是就每次添加都添加在尾部

new node的目的就是为了指向上一个节点,下面的l.next就是为了指向下一个节点

3.手写LinkedList

package com.example.list.list;

//手写LinkedList
public class LinkedList<E> {


    private Node<E> first; //第一个节点
    private Node<E> last;//最后一个节点
    int size = 0;//LinkedList存放元素的个数

    private static class Node<E> {
        private E item; //当前结点的值
        private Node<E> prev;//上一个结点
        private Node<E> next;//下一个节点

        /**
         * @param item 当前节点的值
         * @param prev 当前节点的上一个节点
         * @param next 当面结点的下一个节点
         */
        public Node(Node<E> prev, E item, Node<E> next) {
            this.item = item;
            this.prev = prev;
            this.next = next;
        }

    }

    public void add(E e) {
        Node l = last; //获取当前链表中最后一个节点
        //创建一个新的node结点
        Node<E> newNode = new Node<>(l, e, null);//指向上一个节点
        last = newNode;
        if (l == null) {
            //如果在链表中没有最后一个节点,链表是为空
            first = newNode;
        } else {
            l.next = newNode; //指向下一个节点
        }
        size++;


    }

    public E get(int index) {
        //下标如果越界,需要
        return node(index).item;
    }

    Node<E> node(int index) {
        if (index < size >> 1) {
            //查询左边的元素
            Node<E> f = first;
            for (int i = 0; i < index; i++) {
                f = f.next;
            }
            return f;
        } else {
            //查询右边元素
            Node<E> l = last;
            for (int i = size - 1; i > index; i--) {
                l = l.prev;

            }
            return l;
        }
    }

    public E remove(int index) {
        return unlike(node(index));
    }

    private E unlike(Node<E> node) {
        /**
         * 1.根据index,查询出对应的node节点,时间复杂度是O(n)
         * 2.删除链表效率比ArrayList高,
         * 3.获取删除的node节点的上一个和下一个节点
         **/
        final E element = node.item;  //获取到删除的节点的元素
        Node<E> prev = node.prev;  //删除节点的上一个节点
        Node<E> next = node.next; //删除节点的下一个节点
        //如果删除的节点的上一个节点是为空,证明他就是第一个元素
        if (prev == null) {
            //删除的节点是头节点
            first = next;
        } else {
            //删除节点上一个节点.next=删除节点的下一个节点
            prev.next=next;
            next.prev=null;
        }
        //如果删除的节点的下一个节点是null,证明他是尾节点
        if(next==null){
            last=prev;

        }else {
            //如果删除的不是为尾部节点,则删除的下一个节点的.上一个节点改成=删除的上一个节点
            next.prev=prev;
            node.next=null;  //gc回收
        }
        size--;

        return element;
    }

    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("3");
        System.out.println(list.get(0));

    }
}

从手写的,我们很清楚的看 出LinkedList事一个双向链表,在add方法也可以直接看出

4. LinkedList的get方法

debug运行

 折半算法,目前的容量>>1(可以理解为除以2,但是不是完全除以2),如果index比整数小,就去离链表头近的一边去查,反之就去靠近尾部的一边去查

我们这个 从尾部开始查询,如果是去index小于size/2为true,就是从头开始查

当i=2的时候我们就找到了

  我们得到3的删一个元素2

 5.LinkedList的remove方法

根据index删除解读

如果删除对象,他就是把所有元素都遍历一遍效率是很低的,一个一个去比较

6.LinkedList的set方法

看见这个是不是 很眼熟,这个就是我们查询方法,把查询的道节点的值换成你要改的值

查询效率低,当然这个也快不了。

  7.LinkedList总结

1.LinkedList是双向链表的list

2.是非线程安全的

3.元素允许为空,允许重复元素

4.删除效率高,查询效率低(根据index的),查询使用的折半算法

5.没有办法扩容

6.实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Royalreairman

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值