zoukankan      html  css  js  c++  java
  • LinkedList源码解析

    1. 底层数据结构

    在 LinkedList 中,定义了一个内部类 Node 来保存每个节点的信息。在这个内部类中,有一个 E 类型的变量,用于存储该节点的值;nextprev 变量存储着前后两个节点的地址,也是这两个变量是每个前后两个节点存在联系。

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

    在 LinkedList 这个类中,定义了 first 和 last 两个变量来指向链表的头结点和尾节点。在源码中,作者还给了注释,由此可以看出,在存储过程中,第一个节点的 prev 是为 null 的,而第一个节点是有存储值的。最后一个节点也是有存储值的,但是最后一个节点的 next 属性是为 null 的。

    /**
     * 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;
    

    2. 常用 API

    2.1 添加元素

    链表中的 add 方法是将指定元素添加到链表的末尾,主要是调用了 linkLast 方法。

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

    linkLast 方法是添加元素的关键,它首先构造一个节点,节点的前一个元素为 last 变量,后一个节点为 null。接着判断头结点是否为空,如果为空,表示该链表中还没有元素,则将头结点指向该节点。此时,头结点的 prev 变量指向的是 last,而 last 为 null,符合上面注释定义的规则。

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

    2.2 删除元素

    remove 函数主要是遍历整个链表,当找到要删除的节点时,使用 unlink 方法删除这个节点。

    public boolean remove(Object o) {
         if (o == 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;
      }
    

    接下来我们就来看一下 unlink 函数的实现。

    该函数首先判断 prev 变量是否为 null,如果为 null,则说明要删除的节点为头结点,删除头结点只需要 first 指向下一个节点即可。如果前一个节点不为 null,则将前一个节点的 next 指向 next 节点。

    接着判断要 next 变量是否为 null,如果为 null,则说明该节点是最后一个节点,那么只要将 last 指向 prev 就可以删除和最后一个节点之间的联系了。如果不为 null,则将下一个节点的 prev 指向要删除节点的前一个节点,即 prev 变量。

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

    2.3 查找元素

    查找元素是通过 get 这个方法来实现的,在方法内部,首先利用 checkElementIndex 方法检查 index 是否在指定范围内,如果不再指定范围内则抛出 IndexOutOfBoundsException 的异常。接着调用 node 方法查找指定元素。

    在 node 方法内部,通过判断需要查找元素是在链表的前半段还是后半段,然后通过 prev 或者 next 遍历来查找,这是提升查找速度的一个小细节。

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

    2.4 迭代器

    集合框架实现迭代器的原理差不多,大致是这个模式,根据底层数据结构的不同实现 Itr 类中的 hasNext、remove、next 方法。

    public Iterator<E> iterator(){
    	return new Itr();
    } 
    private class Itr implements Iterator(){
    	public boolean hasNext(){
    	}
    	public void remove(){
    	}
    	public E next(){
    	}
    }
    
  • 相关阅读:
    mybatis-批量操作数据(list对象 )
    老男孩linux运维视频地址
    常见HTTP请求错误码
    项目定时器如何进行测试
    tomcat指定特定版本的jdk
    tomcat更改端口号and设置cmd别名
    第三方登陆-qq互联
    javascript-初级-day08
    Maven的pom.xml的格式与约束
    mabatis的mapper文件找不到-ssm升级maven常见问题
  • 原文地址:https://www.cnblogs.com/firepation/p/9448037.html
Copyright © 2011-2022 走看看