Java集合框架:LinkedList

简单介绍:

LinkedList 也是List接口的实现类。一样有序且允许重复,但是与ArrayList的结构不同。ArrayList是基于数组的结构。而ArrayList是基于双向链表的结构。链表的元素的内存空间可以不连续。在插入和删除的效率比较高,随机访问的速度比较慢。

时间复杂度:

add:O(1)

remove:O(1)

get:O(n)

set:O(n)

类名定义:

节点类

节点类为实际的存储元素的类,一共有3个变量,item数据域,next后一个节点,prev前一个节点.节点类的构造函数的参数顺序为:前一节点,元素,后一节点。

成员变量

  

LinkedList的成员变量就3个,size实际元素个数,first头节点,last尾部节点,都用transient关键字修饰,意味着在序列化时将不会被序列化

添加元素

void linkLast(E e) {
    //获取last节点
final Node<E> l = last;
    //new一个以last节点为前驱节点的元素
final Node<E> newNode = new Node<>(l, e, null);
    //last指向新的尾部节点。 last
= newNode;
     //如果尾部节点为空,则将first节点指向新的节点
if (l == null) first = newNode;
    //如果尾部元素不为空,则原来的last元素节点的next域为新的节点
else l.next = newNode;
     //元素个数+1,结构修改次数+1 size
++; modCount++; }

下面引用其他博客图示来简要介绍一下add的过程

首先new一个LinkedList实例对象的时候都是Null。list.add(5),先新建一个前驱节点为null,数据域为5,末尾节点为null的节点。last指向这个节点。由于原先的last为Null,所以first也会指向这个节点,即形成了第二行的图示。

list.add(6)。类似的步骤,先获取当前尾部节点(即5节点),新建一个prev域为5节点,数据域为6,next域为Null的节点,last指向这个节点。原先的last不为空,所以5节点next域指向6节点。形成一个双向的指针。

 删除元素

public E remove() {
        return 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;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

不带参数将直接removeFirst,如果没有元素,则抛出

NoSuchElementException异常。否则执行
unlinkFirst(Node<E> first).简要分析:定义element和next节点来存储first指向的元素的item和next。然后直接将first指向的节点的item和next域都置null。然后判断next是否为空,如果不为空,则first=next(first指向下一个节点)
为空的话,直接将prev域也置null就可以了。(源码都标注出了置null是为了方便GC。。哈哈,我一般写代码都直接指向下一个节点断开就好了。。要改。。)
unlinkLast也是类似,就不写了。

带下标的remove:
public E remove(int index) {
        checkElementIndex(index);//检查下标是否越界
        return unlink(node(index));
    }

node(index)根据下标寻找元素的方法

Node<E> node(int index) {
        // assert isElementIndex(index);
      //如果Index小于元素个数的一般,则从first开始,按next的方式往后遍历
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else { //如果index大于等于元素的一半,则从last开始,按prev的方式往前遍历。
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

断开该节点的unlink方法:(这方法其实是比较基本的双向链表remove的方法了。简单注释一下就好了。)

E unlink(Node<E> x) {
        // assert x != null;
     //定义3个常量存储要删除的节点的3个变量的值
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
    //如果prev域为空,意味着当前元素为first节点。直接first指向下一个节点就好。 否则prev域的节点的next域直接指向当前节点的下一节点。当前的节点的prev置null(有点绕,但是用过链表应该都能懂,就是改变指针指向的方法方法而已。)
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
    //如果next域为null,意味着当前节点为last节点,直接last节点指向prev指向的节点就好。否则next节点的prev指向当前节点的prev指向的节点。当前next置null
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
     //最后数据域置null,元素个数-1,结构改变次数+1
        x.item = null;
        size--;
        modCount++;
        return element;
    }

remove(Object o)

 

 修改元素

set方法一样是检查下标,通过node(index)方法遍历到要找的节点,然后替换。返回旧值

获取元素

 

get方法,检查下标,遍历下标找到节点。

 其他

由于LinkedList方法也实现了Deque(双端队列),所以双端队列的方法,push,pop,peek等也可以实现。暂时不介绍了。数据结构写到队列的时候还会提一下。这些都是比较基本的数据结构的方法。

图片来源:https://www.cnblogs.com/leesf456/p/5308843.html

能力有限且暂时时间有限,按照比较个人的理解去写博客,深度也不够,当前目标重在通过阅读源码来加深对集合框架的理解。当前也许也只能看懂逻辑与数据结构的方面的意思,以后会慢慢进步写出不一样的东西。加油!!!!

posted @ 2019-01-26 21:26  发包哥哥  阅读(572)  评论(0编辑  收藏  举报