[java][集合]LinkedList详解及源码分析

LinkedList的基本结构

LinkedList是一个双端链表,继承了AbstractSequentialList,实现了List、Serializable、Cloneable、Deque接口,线程不安全。

链表结构

什么是链表?就像是自行车的链条,一环接一环,如果我们需要寻找其中的某一环,只要我们能找到任意一环就可以找到我们需要的那一环。在链表中的每一环都叫节点,节点氛围三个部分两端分别存储的是前后节点的引用,中间存储的是真正的数据。

在JDK1.6之前,LinkedList是环形链表,JDK1.7进行了优化改为了线性链表

首先看1.7中的结构

《[java][集合]LinkedList详解及源码分析》

再看1.6中的结构

《[java][集合]LinkedList详解及源码分析》

《[java][集合]LinkedList详解及源码分析》

源码分析

成员变量

transient int size = 0;//链表节点的个数
transient Node<E> first;//链表第一个节点
transient Node<E> last;//链表最后一个节点

构造方法

无参构造方法

 public LinkedList() {
    }

参数为一个集合的构造参数以及涉及到的addAll方法。

public boolean addAll(int index, Collection<? extends E> c) {//
    checkPositionIndex(index);//判断是否在指定范围,即>=0 && <size

    Object[] a = c.toArray();//将集合对象转为数组
    int numNew = a.length;//
    if (numNew == 0)//如果numNuw=0,返回false
        return false;
    //创建两个节点对象来存储前驱点(pred)和后驱点(succ)的地址
    Node<E> pred, succ;
    if (index == size) {//如果插入的位置是最后
        succ = null;//将后驱点设置为null
        pred = last;//将最后一个元素的地址值赋给pred
    } else {//如果插入的位置不是最后
        succ = node(index);//将原来index位置上的元素的地址值赋给succ
        pred = succ.prev;//将原来index位置前一个元素的地址值赋给pred
    }

    for (Object o : a) {//遍历数组
        @SuppressWarnings("unchecked") E e = (E) o;
        //第一次循环得到新节点
        Node<E> newNode = new Node<>(pred, e, null);
        if (pred == null)//如果是第一个
            first = newNode;    //将first改为newNode
        else
            pred.next = newNode;//否则将pred的后引用改为nuwNode
        pred = newNode;
    }
    if (succ == null) {//如果succ是null
        last = pred;//则将pred设置为last
    } else {
        pred.next = succ;//将pred的后引用改为succ的地址
        succ.prev = pred;//将succ的前引用改为pred的地址
    }

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

静态内部类: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;
        }
    }

常用方法

clear清除所有元素

    public void clear() {
        //可以看出是将所有元素都设为null
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

实现List接口的索取元素方法

get获得指定下标的元素

    public E get(int index) {
        checkElementIndex(index);//判断下标是否在范围内:>=0 && <size
        return node(index).item;//本质还是集合的遍历,和ArrayList不一样,ArrayList本质是数组可以直接利用下标得到元素
    }

   Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {//判断下标在聊表的什么位置,如果在前半部分就从0遍历到下标位置,如果是后半部分,就从下标位置遍历到最后
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;//获得后一个节点
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;//获得前一个节点
            return x;
        }
    }

实现Deque接口的获取元素方法

getFirst获取头元素

    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

peek获取第一个元素和getFirst方法区别是peek方法不会抛异常

    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

peek获取第一个元素

    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }

getLast获取尾元素

    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

peekLast获取尾元素

    public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }

set根据指定下标替换元素

    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

实现List接口的添加操作

add在尾部添加元素

    public boolean add(E e) {
        linkLast(e);
        return true;
    }

add的重载方法在指定位置添加元素

    public void add(int index, E element) {
        checkPositionIndex(index);//检查是否在[0,size)之间
        if (index == size)//若果是在链表尾部
            linkLast(element);
        else//添加在链表中间
            linkBefore(element, node(index));
    }
    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;//链表不为空,那么将该节点作为原链表尾部(原来的last后驱点为null)的后驱点
        size++;
        modCount++;
    }
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;//创建节点指向原链表下标前面一个节点
        final Node<E> newNode = new Node<>(pred, e, succ);//创建新节点,前驱点指向刚才建的节点,后驱点指向原链表下标的节点
        succ.prev = newNode;//将原链表下标的节点前驱点指向新节点
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

《[java][集合]LinkedList详解及源码分析》《[java][集合]LinkedList详解及源码分析》

push头部添加元素,是一种堆栈方法

    public void push(E e) {
        addFirst(e);
    }

实现Deque接口的添加方法

addFirst在头部添加元素

    public void addFirst(E e) {
        linkFirst(e);
    }

    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

offerFirst在头部添加元素

    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

addLast在尾部添加元素

    public void addLast(E e) {
        linkLast(e);
    }
    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++;
    }

offerLast在尾部添加元素

    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

offer添加元素

    public boolean offer(E e) {
        return add(e);
    }

实现Deque接口的删除方法

removeFirst删除第一个元素

    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;//得到第一个元素的值用来做返回值
        final Node<E> next = f.next;//新建一个next节点指向第二个元素
        f.item = null;//将一个节点设置为null
        f.next = null; //将第一个节点的后驱点设为null
        first = next;//将原链表的第二个节点设为新链表的第一个节点
        if (next == null)
            last = null;
        else
            next.prev = null;//将原链表的第二个节点的前驱点设为null
        size--;
        modCount++;
        return element;
    }

poll删除第一个元素和removeFirst的区别是不会抛出异常

    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

pollFirst删除第一个元素

    public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

pop删除第一个元素是一种堆栈方法

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

removeLast删除最后一个节点

    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

pollLast删除最后一个元素

    public E pollLast() {
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }

实现List接口的删除方法

remove删除元素

    public boolean remove(Object o) {
        if (o == null) {//如果对象为null
            for (Node<E> x = first; x != null; x = x.next) {//遍历链表找到为null的元素
                if (x.item == null) {
                    unlink(x);//删除元素
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {//遍历链表找到和对象一样的元素
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    E unlink(Node<E> x) {//删除节点
        final E element = x.item;//获得节点的的真正数据
        final Node<E> next = x.next;//创建节点next指向x的后一个节点
        final Node<E> prev = x.prev;//创建节点prev指向x的前一个节点

        if (prev == null) {//若prev是null,表示x是第一个元素
            first = next;//将next设为新链表的第一个节点
        } else {
            prev.next = next;//将prev的后驱点指向next
            x.prev = null;//x的前驱点设为null
        }

        if (next == null) {//如果next是null,表示x是最后一个元素
            last = prev;//将prev设为新链表的最后一个元素
        } else {
            next.prev = prev;//next的前驱点指向prev
            x.next = null;//x的后驱点设为null
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

contains是否包含某个对象

    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

总结LinkedList中删除,添加方法

《[java][集合]LinkedList详解及源码分析》《[java][集合]LinkedList详解及源码分析》

《[java][集合]LinkedList详解及源码分析》

clone创建一个相同内容的副本

    public Object clone() {
        LinkedList<E> clone = superClone();

        // Put clone into "virgin" state
        clone.first = clone.last = null;
        clone.size = 0;
        clone.modCount = 0;

        // Initialize clone with our elements
        for (Node<E> x = first; x != null; x = x.next)
            clone.add(x.item);

        return clone;
    }

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;
    }

ListIterator迭代器方法双向迭代器

    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/NGUhuang/article/details/80385048
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞