LinkedList源码剖析~

包路径:package java.util;

一、基本属性

1、transient int size = 0;      表示当前集合存储的元素个数

2、  transient Node<E> first;     表示第一个结点

3、 transient Node<E> last;       表示最后一个结点

二、构造函数

1、有参构造如下:传入的参数是集合类型的对象,首先调用自身的无参构造方法,构造一个空的链表,其次采用了addAll()方法,将集合类型的对象一一添加进去。

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

2、无参构造如下:

public LinkedList() {
    }

三、继承关系

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

继承了AbstractSequentialList抽象类

实现了List接口(提供List接口中所有方法的实现)

实现了Dequq接口,实现了Dequq的所有可选操作

实现了Cloneable接口,它支持克隆(浅拷贝),底层实现:LinkedList节点并没有被克隆,只是通过Object的clone()方法得到的Object对象强制转化成LinkedList,然后把它内部的实例域全部置成空,然后把被拷贝的LinkedList节点中的每一个值都拷贝到clone中。

实现了java.io.Serializable接口,表示支持可序列化(底层提供两个方法:readObject()、writeObject()都可用于实现序列化)

四、方法

1、LinkedList中真正用来存储元素是通过一个内部类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;
        }
    }

2、添加元素:

addFirst(E e)基于 头插法:linkFirst(E e)参数中的元素作为链表的第一个元素、

addLast(E e)基于尾插法:linkLast(e)参数中的元素作为链表中的最后一个元素、

add(E e)基于尾插法:linkLast(e)、

addAll(Collection<? extends E> c)基于:addAll(size, c)、

add(int index, E element) 基于尾插法:linkLast(e)或者基于:linkBefore(E e, Node<E> succ)在非空节点succ之前插入元素e 、(不同情况采用方法不同)

addAll(int index, Collection<? extends E> c)

public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);//判断传进来的位置index是否合法

        Object[] a = c.toArray();//将传进来的集合转化为数组,为Object[] a
        int numNew = a.length;//新建一个变量存储数组的长度
        if (numNew == 0)//如果添加元素的长度是0的话,直接返回,无须进行后面的操作
            return false;


        //定义两个节点pred、succ
        //Node<E> pred:表示待添加节点的位置
        //Node<E> succ:表示待添加节点的前一个位置
       
        Node<E> pred, succ;
        if (index == size) {//添加节点的位置在LinkedList最后一个元素的后面
            succ = null;    //将succ置成空的
            pred = last;    //pred指向尾结点
        } else {            //添加节点的位置在LinkedList中
            succ = node(index);//返回对应索引位置的节点
            pred = succ.prev;  //pred指向succ节点的前一个节点
        }
        
        //接着遍历数组中的每一个元素,在每一次遍历的时候,都新建一个节点,该节点存储数组a中遍历
        //的值,该节点的prev用来存储pred节点,next置为空,接着判断该节点的前一个节点是否为空,
        //如果为空的话,则把当前节点设置为头结点,否则的话把当前节点的前一个节点的next值设置为当
        //前节点,最后把pred设置为当前节点,以便后续新节点的插入
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        //这里仍然分为两种情况
        //情况一:succ==null,也就是新添加的节点位于LinkedList最后一个元素的后面,通过遍历a中
        //的所有元素,此时pred指向LinkedList中的最后一个元素,所以把last指向pred指向的节点。
        //情况二:succ不等于空的时候,也就是表明在LinkedList中添加的元素,需要把pred的next指
        //向succ上,succ的prev指向pred。
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew; //集合的大小设置为新的大小
        modCount++;     //modCount(修改的次数)自增
        return true;
    }

3、删除元素:

 removeFirst() :基于头部删除:unlinkFirst()     删除LinkedList中的第一个节点;

removeLast():基于尾部删除: unlinkLast()      删除LinkedList中的最后一个节点;

remove(Object o) :基于:unlink()   删除指定的元素;

remove(int index) :基于:unlink(node(index))   按照位置进行删除;

 removeFirstOccurrence(Object o): 基于:remove(o);移除第一次出现的元素。(从前向后遍历集合) 底层调用remove()方法,通过从前向后遍历集合。

removeLastOccurrence(Object o):基于:unlink();移除第一次出现的元素。(从后向前遍历集合) 

remove() :基于:removeFirst();

4、实现双端队列接口的方法:

peek() :返回头结点的值(即使头结点为空,也直接返回)

element():返回头结点的值(和上面的实现方式不一样,如头结点为空,抛出异常)

poll():移除头结点,并返回移除的头结点的值

remove():移除元素(移除头结点的元素,如果为空,则抛出异常)

offer(E e):在链表的尾部添加元素

offerFirst(E e):在链表的头部添加元素

offerLast(E e):在链表的尾部添加元素

peekFirst() :弹出链表的头结点的元素,如果为空的话,则返回null

peekLast() :弹出链表的尾结点的元素,如果为空的话,则返回null

pollFirst():取出链表头节点的值,并删除该头节点。如果头结点为空,则返回null

pollLast():取出链表尾节点的值,并删除该头节点。如果头结点为空,则返回null

push(E e):增加一个新的元素:(新添加的元素位于LinkedList的头结点)

pop():弹出头结点的元素(删除头结点的值,并返回删除的值)

五、总结

1、LinkedList底层采用双向链表的存储结构。

2、LinkedList既可以从前向后遍历,又可以从后向前遍历。

3、LinkedList可以存储重复数据

4、LinkedList可以存储null值

5、LinkedList插入数据有序

猜你喜欢

转载自blog.csdn.net/qq_40303781/article/details/83897971