LinkedList源码 底层原理实现

LinkedList底层实现

LinkedList底层实现为一个双向链表。如下图所示
在这里插入图片描述

  • 每个节点有前驱prev和后继next节点,以及节点保存的值item.
    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;
        }
    }
    
  • 双向链表有头节点first、尾节点last以及链表长度size。
    public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
          
          
    transient int size = 0;
    
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;
    
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
    
  • first 指向第一个节点,first.prev为null。
  • last 指向最后一个节点,last.next为null。
  • 当链表中没有数据时,first 和 last 是同一个节点,前后指向都是null。

初始化 构造函数

 transient Node<E> last;

    /**
     * Constructs an empty list.
     * 创建一个空的链表
     */
    public LinkedList() {
    
    
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     * 按照集合迭代器返回的顺序,构造一个包含指定集合元素的链表。
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
    
    
        this();
        //内部调用public boolean addAll(int index, Collection<? extends E> c) 方法
        addAll(c);
    }

增加、删除、查找、更新等方法都用到了这个node(index)函数,有必要提一下。按照传入的索引返回Node节点。
if (index < (size >> 1))这个if用来判断index处于数组的前半段还是后半段。如果index在前半部分则从头遍历,否则从尾遍历。

Node<E> node(int index) {
    
    
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
    
    
            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;
        }
    }

增加元素

代码都比较简单,都是在私有方法中完成操作,然后提供public方法作为接口。

  1. addFirst(E) 头部添加
	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++;
    }
  1. addLast(E) 尾部添加
	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++;
    }
  1. add(E) 尾部添加
    调用的尾部添加 linkLast(e);
	public boolean add(E e) {
    
    
        linkLast(e);
        return true;
    }
  1. add(int,E) 按索引位置插入
	public void add(int index, E element) {
    
    
		//判断索引范围
        checkPositionIndex(index);

        if (index == size)
        	//尾插
            linkLast(element);
        else
        	//获取索引位置的节点,指定位置插入
            linkBefore(element, node(index));
    }
    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++;
    }
  1. addAll 批量添加与批量插入
	//尾部批量添加,还是调用下方这个在指定位置批量插入的函数
	public boolean addAll(Collection<? extends E> c) {
    
    
        return addAll(size, c);
    }
	
	//在指定位置批量插入
	public boolean addAll(int index, Collection<? extends E> c) {
    
    
        //index范围检测
        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 {
    
    
        	//node(index)返回指定位置节点。
            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)
                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;
    }

删除元素

删除节点大致有以下几种方式。

	//删除头节点,并返回该节点的值
	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 boolean remove(Object o) {
    
    
        if (o == null) {
    
    
        	//删除第一个node.item=null的节点
            for (Node<E> x = first; x != null; x = x.next) {
    
    
                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;
    }
    //删除索引指向的节点,并返回该节点的值
    public E remove(int index) {
    
    
        checkElementIndex(index);
        return unlink(node(index));
    }
    //删除头节点,并返回该节点的值
    public E remove() {
    
    
        return removeFirst();
    }

可以发现这些方法中调用了一些封装的方法,如unlinkFirst、unlinkLast,unlink三种方法。在这三种方法中,完成了对链表的节点具体的删除操作。


    /**
     * Unlinks non-null first node f.
     * 删除头节点
     */
    private E unlinkFirst(Node<E> f) {
    
    
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

    /**
     * Unlinks non-null last node 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;
    }

    /**
     * Unlinks non-null node x.
     * 删除指定节点。
     */
    E unlink(Node<E> x) {
    
    
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
    
    
            first = next;
        } else {
    
    
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
    
    
            last = prev;
        } else {
    
    
            next.prev = prev;
            x.next = null;
        }

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

查询操作

查询操作就比较简单。

  • getFirst()返回头节点的值first.item。
  • getLast()返回尾节点的值last.item。
  • get(int index)返回index索引位置的节点的值,也是调用的node(index)方法获取该位置的节点。
  • 一般的方法中若传入index索引,都需要对index进行范围判断。
	public void addFirst(E e) {
    
    
        linkFirst(e);
    }
    public void addLast(E e) {
    
    
        linkLast(e);
    }
    public E get(int index) {
    
    
    	//index判断
        checkElementIndex(index);
        //node(index)返回指定节点。
        return node(index).item;
    }

更新操作

更新节点值set(index , E)修改指定位置的节点的值item。
分三步:

  • index是否合法判断。
  • 调用node(index)返回该节点。
  • 修改该节点的值item。
 public E set(int index, E element) {
    
    
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

其他常用API

LinkedList还有其他一些常用API,列举一下:

  1. peek():返回头节点的值first.item。如果头节点为空则返回null
  2. element(): 返回头节点的值,如果头节点为null则 throw NoSuchElementException异常。
  3. poll(): 返回头节点的值并删除头节点,如果链表为空则返回null。
  4. offer(E):尾部增加元素。
  5. push(E):头部增加元素。
  6. pop():删除头节点并返回其值。如果链表为空throw NoSuchElementException异常。
  7. clone():返回LinkedList的浅拷贝。

猜你喜欢

转载自blog.csdn.net/Zhangxg0206/article/details/111294423