源码解读--LinkedList

21 篇文章 0 订阅
13 篇文章 0 订阅

一.序言

在上一篇队列中,只讲解了基本原理,今天就来聊聊队列的一个实现,也是面试中经常和ArrayList对比的一个类--LinkedList。java源码系列

他们的不同点:

1.实现结构不同。ArrayList 是基于数组的list。LinkedList是基于链表的list。

2.初始容量不同。ArrayList初始容量是10,之后按1.5倍扩容。LinkedList没有初始容量一说。

3.访问方式。ArrayList是基于随机访问的,查询会比LinkedList快,但是添加和删除比LinkedLis慢。

看看LinkedList图谱:


LinkedList是队列和列表的集合,他俩的特性它都有。它由一个一个的Node链起来,是一个双向链表。

                             

二.源码解读

1.初始化

public LinkedList() {  第一种初始化方式
}
public LinkedList(Collection<? extends E> c) {  //添加一个集合来构造自己
    this();
    addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);  //添加
}
public boolean addAll(int index, Collection<? extends E> c) {  //看看是怎么添加的
    checkPositionIndex(index);  //检查一下索引是否在0和队列长度之间,不然要抛出数组越界异常

    Object[] a = c.toArray();  
    int numNew = a.length;  //保存数组的长度
    if (numNew == 0)   //长度为0 就相当于空的LinkedList构造
        return false;

    Node<E> pred, succ;  
    if (index == size) {  
        succ = null;
        pred = last;  //pred 指向队列的尾部
    } else {
        succ = node(index);  //找到索引的节点,如果索引的index小于队列长度的一半,从前往后找也就不断的next找,反之,则往前找prev
        pred = succ.prev;   //pred 现在指向了succ的前一个元素
    }

    for (Object o : a) {  //遍历数组 
        @SuppressWarnings("unchecked") E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);  //创建一个新的node,尾节点指向为空,前一个节点就是pred
        if (pred == null)   //如果之前队列是空的,队列的头结点(first)就指向刚创建的节点
            first = newNode;
        else
            pred.next = newNode;   //一个一个的往后添加节点
        pred = newNode;  
    }

    if (succ == null) {  //如果队列为空的话,直接last指向pred就可以了
        last = pred;
    } else {
        pred.next = succ;   //补上succ和后面的值
        succ.prev = pred;
    }   //简单的归纳一下就是将原链表劈成了两半,将新添加的集合拼在前一段,后面也就是succ指定的节点再拼接在链表上面。比如之前队列是a->b->c,在第二个位置插了g,h 变成了a->g->h->b->c

    size += numNew;  //队列加上集合的个数
    modCount++;  //操作记录数加1
    return true;
}
2.添加值

它的添加方法有这么多。,还有offer(e),就选offer(E)来说吧!

public boolean offer(E e) {
    return add(e);
}

public boolean add(E e) {  //添加一个元素,队列是添加在队列尾部
    linkLast(e);
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;   //l指向了队列的尾部
    final Node<E> newNode = new Node<>(l, e, null);  //新建一个节点,它的前一个是之前的队列的队尾,下一个为空
    last = newNode;  //队列last现在变成了这个新的节点
    if (l == null)  //如果是空队列,头结点也指向这个新建的节点
        first = newNode;
    else
        l.next = newNode;  //之前的队列不为空,队尾的下一个指向新建的节点
    size++;  //添加了一个,数值加一
    modCount++;
}

addFist(E)方法是在队列头插入,反队列设计,但是有时会有这样的设计?可以用栈替代吧!其实想想,还真有这样的需求。

如果用插入a,b,c都需要他们是顺序输出的,但是这个又有需求过来了,d要插入,并且要放在第一位。

3.出队列

出队列的方法也有很多,就拿poll来说吧。

public E poll() {
    final Node<E> f = first;   //拿到第一个值,看看是不是空,如果是空,直接放回空就好了,不是空,就删除第一个
    return (f == null) ? null : 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; // 方便gc,可达性分析,没有引用就是垃圾
    first = next;  //队列的已经指向了下一个
    if (next == null)  //就只有一个元素,last就是空
        last = null;
    else
        next.prev = null;  //第一个的next指向了null,也就是前面没有元素了
    size--;  //队列头减少了一个节点
    modCount++;
    return element;
}

4.获取节点

public E peek() {
    final Node<E> f = first;  //直接拿到first,如果是空就返回null,否则返回里面元素值。LinkedList里面建了一个first,这样就可以直接拿,拿到最后一个值,是不是也很容易,把first替换成last就可以了,时间复杂度为o(1),效率提高了。
    return (f == null) ? null : f.item;
}

只看了peek方法,其他方法差不多。

到此,LindkedList基本源码差不多将完了,你懂了吗?

总之请记住队列的特点,先进先出(后进后出),以不变应万变!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值