容器(六):最佳替补LinkedList

容器(六):最佳替补LinkedList

标签: Java编程思想


在日常开放中,我们使用的最多的是ArrayList。ArrayList是以数组的方式实现的,在数据结构上就是顺序表,是直接针对数组下标随机访问的,一次在遍历时速度飞快;但是在插入、删除时要整体移动,这样的效率略差。

当这是最佳替补LinkedList出场了,LinkedList,的数据结构是链表,我们都知道链表的特性:在插入删除等操作时只需要改变前后节点的指针域的指向即可。在元素数量不是很多(几万),需要频繁的插入和删除时,就可以使用LinkedList,这样比较省事。

至于为什么说“元素数量不是很多”的情况下,这是因为链表的存储空间不是连续的,当元素在几万以上时,光是寻址就要花费很多时间,而ArrayList通过下标马上就能找到。所以到底如何选择,要看具体的应用场景,也要亲自去实践。

LinkList的出身

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

AbstractSequentialList

LinkList继承于AbstractSequentialList,这是一个继承于AbstractList类的,按次序的,简化实现List接口的抽象类

AbstractSequentialList 在 AbstractList 的基础上实现了以下方法:

构造器

只有一个唯一的构造函数。 (用于子类构造函数的调用,通常是隐式的。)

protected AbstractSequentialList() {
    }
添加元素

add(int, E): 添加元素到指定位置,将当前处于该位置(如果有的话)和任何后续元素的元素移到右边(添加一个到它们的索引),通过调用ListIterator.add(),实现了父类的方法:

public void add(int index, E element) {
    try {
        //通过调用ListIterator.add(),实现了父类的方法
        listIterator(index).add(element);
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}

addAll(int index, Collection<? extends E> c):用获取到的 listIterator 逐个添加集合中的元素:

public boolean addAll(int index, Collection<? extends E> c) {
        try {
            boolean modified = false;
            ListIterator<E> e1 = listIterator(index);
            Iterator<? extends E> e2 = c.iterator();
            while (e2.hasNext()) {
                e1.add(e2.next());
                modified = true;
            }
            return modified;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
修改元素

set(int index, E element): 修改指定位置的元素为新的:

public E set(int index, E element) {
    try {
        ListIterator<E> e = listIterator(index);
        E oldVal = e.next();
        e.set(element);
        return oldVal;
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}
获取元素

get(int index): 通过迭代器的next()方法获取指定位置的元素:

public E get(int index) {
    try {
        return listIterator(index).next();
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}
删除元素

remove(int index): 删除指定位置的元素:

public E remove(int index) {
    try {
        ListIterator<E> e = listIterator(index);
        E outCast = e.next();
        e.remove();
        return outCast;
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}
总结

可以看到, AbstractSequentialList 把父类 AbstractList 中没有实现或者没有支持的操作都实现了,而且都是调用的 ListIterator 相关方法进行操作。

我们知道支持 RandomAccess 的对象,遍历时使用 get 比 迭代器更快。

而 AbstractSequentialList 只支持迭代器按顺序 访问,不支持 RandomAccess,所以遍历 AbstractSequentialList 的子类,使用 for 循环 get() 的效率要 <= 迭代器遍历:

Deque接口

public interface Deque<E> extends Queue<E>

Deque 是 Double ended queue (双端队列) 的缩写,读音和 deck 一样。

Deque 继承自 Queue,直接实现了它的有 LinkedList, ArayDeque, ConcurrentLinkedDeque 等。

Deque 支持容量受限的双端队列,也支持大小不固定的。一般双端队列大小不确定。

Deque 接口定义了一些从头部和尾部访问元素的方法。比如分别在头部、尾部进行插入、删除、获取元素。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。

下表总结了上述 12 种方法:

方法 头部抛出异常 头部特殊值 尾部抛出异常 尾部特殊值
插入 addFirst(e) offerFirst(e) addLast(e) offerLast(e)
移除 removeFirst pollFirst() removeLast() pollLast()
检查 getFirst() peekFirst() getLast() peekLast()

此接口扩展了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:

Queue 方法 等效 Deque 方法
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()

双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于Deque 方法,如下表所示:

堆栈方法 等效 Deque 方法
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

注意,在将双端队列用作队列或堆栈时,peek 方法同样正常工作;无论哪种情况下,都从双端队列的开头抽取元素。

此接口提供了两种移除内部元素的方法:removeFirstOccurrence 和 removeLastOccurrence。

与 List 接口不同,此接口不支持通过索引访问元素。

虽然 Deque 实现没有严格要求禁止插入 null 元素,但建议最好这样做。建议任何事实上允许 null 元素的 Deque 实现用户最好不 要利用插入 null 的功能。这是因为各种方法会将null 用作特殊的返回值来指示双端队列为空。

Deque 实现通常不定义基于元素的 equals 和 hashCode 方法,而是从 Object 类继承基于身份的equals 和 hashCode 方法。

LinkedList 的实现

LinkedList所实现的数据结构是一个双向链表,我们在数据结构中学到了,双向链表有一个头结点,一个尾节点,还有中间节点,节点有一个数据域,一个前指针域prev和一个后指针域next,分别指向前后两个节点。

在LinkedList中有三个成员变量,确定了链表的大小和头尾节点:


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值