LinkedList中查询(contains)和删除(remove)源码分析

1、contains源码分析

本文分析双向链表LinkedList的查询操做源码实现。jdk中源程序中,LinkedList的查询操做,经过contains(Object o)函数实现。具体见下面两部分程序:
java

public boolean contains(Object o) {
    return indexOf(o) != -1;
}

node

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

indexOf函数查询对象o在链表中的索引位置。编程

源码首先将元素为null的情形单独判读剥离,我的分析,应该是若是不单独分析,null元素在下边的for循环中,不能执行o.equals操做(编译器输入null.,会自动将已有输入替换为NullPointerException.,提示空指针异常)。数组

因为链表不一样于数组,在内存中并不是连续存储,所以访问某个元素须要遍历。源程序中使用for循环进行遍历。index表示链表元素索引,初值为0。函数

一、针对空元素(null)的状况,用for循环遍历,查找元素为null的节点,并返回索引index。for循环初始条件为头指针(无元素),判断条件为节点不为null,自增表达式为x重赋值为下个节点(利用节点x的next指针实现,在java中next为node对象的属性成员)。每次自增,index+1。
二、针对非空元素,遍历操做同上。函数结束的判断条件变为o.equals(x.item),这里equals方法为Object超类的方法,程序中元素类型非Object也可调用。源码分析

两种情形下,链表遍历完毕(仍为return),代表该元素o在链表中不存在,所以返回-1。学习

2、remove源码对比

经过查阅,发现remove方法和contains方法的源码实现很是类似,列出以下:this

/**
 * Removes the first occurrence of the specified element from this list, 
 * if it is present.  If this list does not contain the element, it is
 * unchanged. 
 * @param o element to be removed from this list, if present
 * @return {@code true} if this list contained the specified element
 */

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

对比发现,和contains相似,remove操做首先也是查找Object o是否存在与表中。空节点元素和非空节点的循环判断基本相同,在找到Object o后,区别与contains的返回索引,remove须要删除节点link(LinkedList的删除须要对next链进行重配置引用)。spa

删除节点的方法unlink的源码以下:指针

//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++;  //注:已从结构上修改此列表的次数。AbstractList属性
    return element;
}

分析:
删除节点程序中,须要考虑节点在链表中的不一样位置,进行不一样的操做。

一、节点于链表首端

clipboard.png

此时,特殊操做是须要将first指针(引用)指向其后的节点。

first = next;

二、节点于链表尾端

clipboard.png

此时,须要将上一个节点的next指针指向null,即上个节点称为尾节点。

last = prev;

三、节点于链表中间状况

clipboard.png

若是不仔细思考,咱们在写程序时,会直接在两个if判断后,将剩余的相关操做写在一块儿。而源码并无这么作,稍做分析,就能看出,若是前一个if中,条件是prev,所以可对该节点的prev进行相关操做。然后续的if语句,还需判断该节点的next引用,所以在第一个if-else语句体中,不对next进行操做,不然更改后,后学的next已经不是该节点的原next值。

所以,第一个if-else中:

prev.next = next;
x.prev = null;

第二个if-else中:

next.prev = prev;
x.next = null;

3、总结

从LinkedList的源码能够看到,源程序中相应方法代码简洁,逻辑清晰正确,在学习java的过程当中,除了学习知识为我所用,也要避免闭门造车,多查看源码和其余优秀的项目程序,参考优秀的编程习惯和思想,从而积累经验,提升本身的水平。

相关文章
相关标签/搜索