LinkedList
LinkedList的用法
LinkedList的构造方法与ArrayList类似,有两个,一个是默认构造方法,一个是可以接受一个已有的Collection,如下
public LinkedList();
public LinkedList(Collection<? extends E> c)
//例如可以这么创建
List<String> list=new LinkedList<>();
List<String> list2=new LinkedList<>(
Arrays.asList(new String[]{"a","b","c"});
)
LinkedList与ArrayList一样,同样实现了List接口,而List接口扩展了Collection接口,Collection又扩展了Iterable接口,所以使用方法跟上一篇博客一样,同时LinkedList还实现了Deque和Queue接口,所以我用Java刷算法题的时候喜欢用LinkedList向上转型成Deque作为栈或队列使用。
Queue扩展了Collection,主要有三个操作:
- 在尾部添加元素(add,offer)
- 查看头部元素(element,peek),返回头部元素,但不改变队列
- 删除头部元素(remove,poll),返回头部元素,并从队列删除
Deque扩展了Queue,包括了栈的操作方法(入栈push,出栈pop,查看栈头部元素peek),还有更为明确的操纵两端的方法。
实现原理
上一篇博客讲过,ArrayList内部是动态数组,元素在内存是连续存放的,而LinkedList内部是双向链表,元素通过链接连在一起。因此,LinkedList查找元素的时候,必须从头或尾顺着链表链接查找,效率比较低。
LinkedList特点分析
用法上,LinkedList是一个List,但也实现了Deque接口,可以作为队列、栈和双端队列使用。原理上,内部是一个双向链表,有着如下特点
- 按需分配空间,不需要预先分配很多空间,而ArrayList则需要预先分配空间
- 不可以随机访问,按照索引位置访问效率很低,必须从头或尾顺着链表顺序查找,O(N/2)
- 按照内容查找效率较低,O(N)
- 在两端添加、删除元素效率很高,为O(1)
- 在中间插入删除元素,要先定位,效率很低,为O(N),但是修改本身效率很高O(1)
ArrayDeque
实现原理
ArrayDeque是一个双端队列是实现类,是基于循环数组实现的。一般而言,由于需要移动元素,数组的插入和删除效率较低,但ArrayDeque的效率却非常高。ArrayDeque实现了Deque接口,跟LinkedList一样,队列长度也是没有限制的。
ArrayDeque内部主要有以下实例变量:
private transient E[] elements;
private transient int head;
private transient int tail;
elements就是存储元素的数组,ArrayDeque的高校来源于head和tail这两个变量,是物理上的简单数组变成了逻辑上的循环数组。避免了头尾操作时的移动。循环数组这里就不多做解释了,ArrayDeque内部的循环数组是动态扩展的,通过head和tail变量维护数组的开始和结尾,数组长度为2的幂次方,使用高校的位操作进行各种判断,以及对head和tail进行维护。
ArrayDeque特点分析
- 在两端添加、删除元素效率很高,动态扩展需要的内存分配以及数组复制开销可以被平摊,因此,添加N个元素的效率为O(N)
- 根据元素内容查找和删除效率较低,为O(N)
- 没有索引的概念,不能根据索引的位置进行操作