LinkedList 双向链表源码分析

目录

  • LinkedList介绍
  • Node内部类
  • LinkedList源码分析

LinkedList 介绍

在分析缓存淘汰算法LRU时,双向链表是其中一种实现方式,动手实现时才发现head和tail在没有其他结点时的处理很别扭,所以就研究下LinkedList的源码,找点思路;

首先看看关于LinkedList的简介

LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。


Node内部类源码

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

Node类很好理解,是个泛型类,由三要素构成:

  • 当前结点的前继结点
  • 当前结点的后续结点
  • 当前结点对应的泛型值;

Node结点在创建实例对象时,就需要对其前后结点以及结点对应的值都初始化;


LinkedList 类源码

注意理解双向链表的核心设计思路,LinkedList的first和last都只是指向链表中的node结点实例对象的引用,first和last自身并没有被创建成单独的结点对象,理解这一点很关键;

实例变量域

// 当前链表的元素个数
transient int size = 0;

protected transient int modCount = 0;

// 指向链表的第一个结点
transient Node<E> first;

// 指向链表的最后一个结点
transient Node<E> last;

 

linkLast方法:向链表尾部添加结点

    /**
     * Links e as last element.
     */
    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++;
    }

// ------------------ 当向链表中首次添加结点时 上述方法可简化为:---------------------------
void linkLast(E e) {
        final Node<E> l = last = null;
        final Node<E> newNode = new Node<>(null, e, null);
        last = newNode;
        first = newNode;
        size++;
        modCount++;
}
// ------------------ 当之后再向链表中添加结点时 上述方法可简化为:-------------------------
void linkLast(E e) {
        final Node<E> newNode = new Node<>(last, e, null);
        final Node<E> l = last;
        last = newNode;
        l.next = newNode;
        size++;
        modCount++;
}

在链表为空时,first和last结点都是指向null;

当第一次向链表尾部添加结点时,

  • 创建一个新结点,prev和next都指向null;
  • 由于此时first和last都没有任何指向(都为null),所以讲first和last都指向该新结点;

当第二次(i > 1)向链表尾部添加结点时,

  • 创建一个新结点,prev指向last指向的旧的尾结点,next指向null;
  • last重新指向新尾结点;
  • last之前指向的尾结点,将其next指向新的尾结点;

linkFirst方法:向链表头部添加结点

    /**
     * Links e as first element.
     */
    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++;
    }

当第一次向链表头部添加结点时,

  • 创建一个新结点,prev和next都指向null;
  • 由于此时first和last都没有任何指向(都为null),所以讲first和last都指向该新结点;

当第二次(i > 1)向链表头部添加结点时,

  • 创建一个新结点,prev指向null,next指向first指向的旧的头结点;
  • first重新指向新的头结点;
  • 旧的头结点的prev指向新的头结点;

unlink方法:从链表中移除一个指定的非null结点

    /**
     * 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) {
            // 如果node是第一个结点,就将第二个结点作为first
            first = next;
        } else {
            // 如果node不是第一个结点,就将前驱结点next指向后继结点;
            // 并剪断x对前驱结点的引用,有利于GC回收
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            // 如果node是最后一个结点,就将倒数第二个结点作为最后一个结点
            last = prev;
        } else {
            // 如果node不是最后一个结点,就将后继结点前驱指向前驱结点;
            // 并简单x对后继结点的引用,有利于GC回收
            next.prev = prev;
            x.next = null;
        }
        // 截断x对item泛型内容的引用,有利于GC回收,至此x结点已经彻底剪断对所有资源的引用
        x.item = null;
        size--;
        modCount++;
        // 从链表中删除x结点后,返回对x的item内容的引用,以备他用
        return element;
    }

 

remove方法:根据指定的内容,删除对应的结点

    // remove根据指定的内容o,来移除相应的结点,remove是基于unlink方法实现移除功能;
    // remove支持移除结点item为null的情景
    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;
    }

 


参考文献

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
源码,经典。 CARD *myinsert(LCARD *head, LCARD *insert) { LCARD *temp = NULL; if (head==NULL)//链表为空 { head = insert; insert->next = insert; insert->prior = insert; } else//链表非空 { temp = head; if (head->cardnum>insert->cardnum)//插入到头前边,并且把自己当作头 { head->prior->next = insert; insert->prior = head->prior; insert->next = head; head->prior = insert; head = insert; } if (insert->cardnum0<50)//小于50正向插入 { while ((temp->cardnum<insert->cardnum)&&(temp->next!=head))//循环 { temp = temp->next; } if (temp->cardnum>insert->cardnum)//第一个条件终止的 { temp->prior->next = insert; insert->prior = temp->prior; insert->next = temp; temp->prior = insert; } else//第二个条件终止的 { head->prior->next = insert; insert->prior = head->prior; insert->next = head; head->prior = insert; } } else//大于50反向插入 { while ((temp->cardnum>insert->cardnum)&&(temp->prior!=head))//循环,第二个条件禁止跑飞 { temp = temp->prior; } if (temp->cardnum<insert->cardnum)//只有第一个条件可以终止的 { temp->next->prior = insert; insert->next = temp->next; insert->prior = temp; temp->next = insert; } } } //printf("%d\t%d\n", insert->id, insert->cardnum); return head; } void swap_id(SWID *sw) { LCARD *temp = sw->head; if (sw->head->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); sw->head->id = sw->inID; return ; } if ((sw->swapcardnum0)<50) { while ((temp->cardnum!=sw->swapcardnum)&&(temp->next!=sw->head)) { temp = temp->next; } if (temp->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); temp->id = sw->inID; } } else { while ((temp->cardnum!=sw->swapcardnum)&&(temp->prior!=sw->head)) { temp = temp->prior; } if (temp->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); temp->id = sw->inID; } } } LCARD *mydel(LCARD *head, LCARD *del) { LCARD *temp = NULL; if (head==NULL)//没有链表 { printf("there is no card\n"); } else//有链表 { if(head->next==head)//链表里就有一个节点并且为头结点 { if (head->cardnum==del->cardnum) { free(head); head = NULL; } else { printf("in mydel error\n"); } } else//链表里有超过一个的节点 { temp = head; if (del->cardnum0<50)//成立则正向删除 { while ((temp->cardnum!=del->cardnum)&&(temp->next!=head)) { temp = temp->next; } if (temp->cardnum==del->cardnum) { temp->prior->next = temp->next; temp->next->prior = temp->prior; free(temp); } } else//反向删除 { while ((temp->cardnum!=del->cardnum)&&(temp->prior!=head)) { temp = temp->prior; } if (temp->cardnum==del->cardnum) { temp->prior->next = temp->next; temp->next->prior = temp->prior; free(temp); } } } } return head; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值