LinkedList源码分析

LinkedList虽然和ArrayList都实现了List接口,但两者的底层数据结构截然不同。从类名可以看出,ArrayList底层数据结构是数组,而LinkedList底层数据结构是双向链表。两者数据结构的优劣如下,ArrayList按下标查询元素速度快,但插入元素或者删除元素效率低,因为都设计到元素的大量移动,而LinkedList插入或删除元素速度较快,查询中间元素效率较低,因为查询元素需要从距离指定位置最近的链表头部或尾部开始遍历(头部距离指定位置近,就从头部开始遍历,反之,从尾部开始遍历),直到遍历到指定位置为止。

下面开始进入到LinkedList内部进行分析它的实现。首先看核心数据结构-双向链表,是通过内部类Entry来定义的。

private static class Entry<E> {
    E element;
    Entry<E> next;
    Entry<E> previous;

    Entry(E element, Entry<E> next, Entry<E> previous) {
        this.element = element;
        this.next = next;
        this.previous = previous;
    }
}

此内部类很简单,总共三个属性,element:存放真正数据;next:指向后继的指针;previous:指向前驱的指针。LinkedList持有了此Entry结构的头元素header,所有的操作都通过header进行操作,另外还有一个整型数值size来记录元素个数。LinkedList属性定义如下:

//傀儡节点,header的element是永远为null的,第一个元素其实是存在了header.next.element中,这个可以从getFirst()方法看出来,header也是不计算在size当中的。
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
//第一个元素其实是存在了header.next.element中
public E getFirst() {
    if (size==0)
        throw new NoSuchElementException();
    return header.next.element;
 }
//最后一个元素就是存在header.previous.element中
public E getLast()  {
    if (size==0)
        throw new NoSuchElementException();
    return header.previous.element;
 }
//add方法是在链表的尾部添加元素,和addLast(E e)方法作用一样
public boolean add(E e) {
    addBefore(e, header);
    return true;
 }
private Entry<E> addBefore(E e, Entry<E> entry) {
    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    newEntry.previous.next = newEntry;
    newEntry.next.previous = newEntry;
    size++;
    modCount++;
    return newEntry;
 }
//获取指定下标处的元素,比较指定的下标距离链表头近还是链表尾近,从距离近的地方开始遍历取值
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);
    Entry<E> e = header;
    //通过位运算计算出链表的中间位置,下标小于中间位置,说明从header往后遍历效率高,如果下标大于(等于从两边都可以)中间位置,说明从header往前遍历效率高
    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的几个主要方法来分析了下此类,其他方法也都比较容易理解。LinkedList的使用场景:在中间插入或者移除元素比较多的场景下使用。如果经常需要按照下标获取元素,则使用ArrayList。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值