LinkedList的方法分析

来源:互联网 发布:杭州程序员招聘信息 编辑:程序博客网 时间:2024/04/28 08:37

LinkedList继承关系
可以看出来LinkedList不仅实现了List接口,还实现了Deque(双端队列),这使得LinkedList可以实现队列的一些特性

// 成员变量:header是一个entry实例,既然知道linkedlist是实现了链表结构,header// 显然是头结点,而且可以看出来,header并不用于存放元素,而是为其他元素提供一个初始// 位置private transient Entry<E> header = new Entry<E>(null, null, null);// size指当前链表中节点个数,不包括头结点private transient int size = 0;....// entry静态内部类private static class Entry<E> {    // 当前节点自身的内容    E element;    // 后一个节点的引用    Entry<E> next;    // 前一个节点的引用    Entry<E> previous;    // 第一个参数,第二个参数是next,第三个是previous,顺序要记住    Entry(E element, Entry<E> next, Entry<E> previous) {        this.element = element;        this.next = next;        this.previous = previous;    }}// 头结点的前一个节点指向自身,后一个节点也指向自身,由这个空链表也可以看出来LinkedList是一个双向循环// 链表public LinkedList() {    header.next = header.previous = header;}// 把集合转化成LinkedListpublic LinkedList(Collection<? extends E> c) {    // 仍然首先定义一个空链表    this();    addAll(c);}

这是前面提到的hashmap内部数组上链表的结构

这里写图片描述

这里写图片描述

LinkedList的内部结构

public boolean addAll(Collection<? extends E> c) {    // 在size处插入集合元素    return addAll(size, c);}// index,从链表的index处(在index-1和index之间)开始插入给定的集合public boolean addAll(int index, Collection<? extends E> c) {    // 如果index为负数或者index大于链表中节点个数,抛出异常    if (index < 0 || index > size)        throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size);    // 集合转数组,如果数组长度为0(集合元素为空),返回false    Object[] a = c.toArray();    int numNew = a.length;    if (numNew==0)        return false;    // 结构变化指标+1    modCount++;// 如果index=size,就在header前插入,如果不是就在index对应的元素前插入// successor是待插入的位置后的节点Entry<E> successor = (index==size ? header : entry(index));// 获得带插入位置的前一个节点,保存predecessor Entry<E> predecessor = successor.previous;// 遍历集合元素    for (int i=0; i<numNew; i++) {        // new一个新的entry,也就是带插入的节点,next为刚刚得到插入位置后的,previous是插入位置前的        // 这个new节点的步骤,实际上完成了插入节点本身前后两个属性引用的指向,也就是说到这个步骤为止        // 根据这个节点可以找到整个链表的元素,但是根据链表的其他节点还不能找到这个新插入的节点,因为和        // 它相邻的节点的next,previous的指向还没有改        Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);        // 前一个节点的next指向新插入的节点,插入完成了2/3        predecessor.next = e;        // 把新插入的节点作为下一个带插入的节点的前一个节点,循环        predecessor = e;}// 循环结束,实际上这时候所有的集合元素,除了最后一个,都已经首尾相连,最后一个元素自身也已经指向原链表的// index处的节点// 插入工作的最后一步,index处的节点,previous引用指向集合的最后一个节点successor.previous = predecessor;    // size加上集合长度    size += numNew;    return true;}
// add方法,试图在linkedlist尾部插入一个元素public boolean add(E e) {    addBefore(e, header);        return true;    }============================= // 在给定index之前插入一个元素  public void add(int index, E element) {  // 如果index=size,那么和直接add到链表末尾一样,调用addbefore插入到header之前  // 如果不是size,调用entry()返回带插入位置的当前节点       addBefore(element, (index==size ? header :entry(index)));    }==============================    // 在给定参数entry节点前插入一个元素,对于header来说,header.previous // 就是链表的最后一个,这也说明了linkedlist实现了一个双向循环链表 private Entry<E> addBefore(E e, Entry<E> entry) { // 构成一个新的节点,previous是entry.previous,next是entry // 对于header来说,新节点已经插入了last和header之间(仅其自身引用指向改变)    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);    // newEntry.previous就是新节点的前一个节点,但是它的next引用目前仍然    // 指向newEntry.next,所以修改之,下面同理。    newEntry.previous.next = newEntry;    newEntry.next.previous = newEntry;    // 节点数+1,结构变化+1,返回新插入的entry    size++;    modCount++;    return newEntry;}

可以看出来add方法调用addbefore完成了在header之前,也就是链表末尾的插入工作。

get(int index)和addBefore(E e, Entry entry)都用到了entry(int index)方法,这个方法实际上是根据index对链表遍历返回对应的节点

 public E get(int index) {        return entry(index).element;    }
private Entry<E> entry(int index) {        if (index < 0 || index >= size)            throw new IndexOutOfBoundsException("Index: "+index+                                                ", Size: "+size);        // header保存在局部变量e                                            Entry<E> e = header;        // 判断index在链表中的位置,跟size/2比较,决定是从前往后遍历还        // 是从后往前遍历        if (index < (size >> 1)) {            for (int i = 0; i <= index; i++)                e = e.next;        } else {            for (int i = size; i > index; i--)                e = e.previous;        }        return e;    }
// 从linkedlist中删除某个元素 public boolean remove(Object o) { // 这里把非空作为分支条件主要是因为o.equals(),会空指针异常        if (o==null) {        // 从header往后遍历,如果为空(e.element==null),就删除该节点            for (Entry<E> e = header.next; e != header; e = e.next) {                if (e.element==null) {                    remove(e);                    return true;                }            }        } else {            for (Entry<E> e = header.next; e != header; e = e.next) {            // 如果element和给定的元素equals返回true,则删除该节点                if (o.equals(e.element)) {                    remove(e);                    return true;                }            }        }        return false;    }===================================private E remove(Entry<E> e) {// header不能删除    if (e == header)        throw new NoSuchElementException();// 把要删除的元素(不是节点),作为删除方法的返回值        E result = e.element;// 修改其头尾节点的next和previous指向,跳过待删除的节点    e.previous.next = e.next;    e.next.previous = e.previous;    // 把删除的节点各个引用属性都改为null,等待gc回收        e.next = e.previous = null;        e.element = null;    //     size--;    modCount++;        return result;    }
0 0
原创粉丝点击