Link-子接口LinkedList
LinkedList基本:
- 底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可重复),包括null
- 线程不安全,没有实现同步
比较 ArrayList和LinkedList :
- 截取此文章一段 传送门 转载的文章基于jdk1.7
LinkedList类是List接口的实现类,它是一个集合,可以根据索引来随机的访问集合中的元素,还实现了Deque接口,它还是一个队列,可以被当成双端队列来使用。虽然LinkedList是一个List集合,但是它的实现方式和ArrayList是完全不同的,ArrayList的底层是通过一个动态的Object[]数组来实现的,而LinkedList的底层是通过链表来实现的,因此它的随机访问速度是比较差的,但是它的删除,插入操作会很快。
ArrayList:
基于数组,在数组中搜索和读取数据是很快的。因此 ArrayList 获取数据的时间复杂度是O(1);
但是添加、删除时该元素后面的所有元素都要移动,所以添加/删除数据效率不高;
另外其实还是有容量的,每次达到阈值需要扩容,这个操作比较影响效率。
LinkedList:
基于双端链表,添加/删除元素只会影响周围的两个节点,开销很低;
只能顺序遍历,无法按照索引获得元素,因此查询效率不高;
没有固定容量,不需要扩容;
需要更多的内存,LinkedList 每个节点中需要多存储前后节点的信息,占用空间更多些。
LinkedList的底层操作机制 :
- LinkedList 底层维护了一个双向链表
- LinkedList中维护了两个属性 first 和 last,分别指向首节点和尾节点
- 每个节点(Node对象),里面有维护了prev,next,item三个属性,实现双向链表
-
简单的一个双向链表:
模拟一个简单的双向链表
/**
* 模拟双向链表
*/
public class LinkedList_doubleLink {
public static void main(String[] args) {
Node n1 = new Node("item01");
Node n2= new Node("item02");
Node n3 = new Node("item03");
n1.next = n2;
n2.pre = n1;
n2.next = n3;
n3.pre = n2;
// 头节点 和 尾节点
Node first = n1;
Node last = n3;
// while 循环 让first指向下一个节点
// 从头到尾
System.out.println("===从头到尾===");
while (true){
if (first == null){
break;
}
System.out.println(first);
first = first.next;
}
// 从尾到头
System.out.println("===从尾到头===");
while (true){
if (last == null){
break;
}
System.out.println(last);
last = last.pre;
}
// 插入 一个节点
// 在n1 和 n2 中间插入一个 m节点
Node m = new Node("插入m");
n1.next = m;
m.pre = n1;
m.next = n2;
n2.pre = m;
System.out.println("==插入节点后==");
first = n1;
while (true){
if (first == null){
break;
}
System.out.println(first);
first = first.next;
}
}
}
// 定义一个Node类,Node对象表示一个节点
class Node{
public Object item;
public Node next;
public Node pre;
public Node(Object name) {
this.item = name;
}
@Override
public String toString() {
return "Node name :" + item;
}
}
LinkedList底层结构
- add(E e) // 在链表后添加一个新元素 通用方法
public boolean add(E e) {
linkLast(e); // 实现的主要方法
return true;
}
/**
* 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; // last应用指向新的节点
if (l == null)
first = newNode;
else
l.next = newNode; // 当前尾节点的 next 指向 新的尾节点
size++;
modCount++;
- remove() :移除链表中第一个元素; 通用方法
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f); // 主要靠这个实现移除
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item; // 将要移除的头节点的信息赋给element,方便最后返回,留用处理。
final Node<E> next = f.next;
f.item = null;
f.next = null; // 断开头节点和下一个节点的尾链接
first = next; // 将头指针对象 指向下一个节点
if (next == null)
last = null;
else
next.prev = null; // 下一个节点断开与头节点的头链接
size--; // 链表减一
modCount++;
return element;
}
常用方法
-
增加: add(E e):在链表后添加一个元素; 通用方法 addFirst(E e):在链表头部插入一个元素; 特有方法 addLast(E e):在链表尾部添加一个元素; 特有方法 push(E e):与addFirst方法一致 offer(E e):在链表尾部插入一个元素 add(int index, E element):在指定位置插入一个元素。 offerFirst(E e):JDK1.6版本之后,在头部添加; 特有方法 offerLast(E e):JDK1.6版本之后,在尾部添加; 特有方法 删除: remove() :移除链表中第一个元素; 通用方法 remove(E e):移除指定元素; 通用方法 removeFirst(E e):删除头,获取元素并删除; 特有方法 removeLast(E e):删除尾; 特有方法 pollFirst():删除头; 特有方法 pollLast():删除尾; 特有方法 pop():和removeFirst方法一致,删除头。 poll():查询并移除第一个元素 特有方法 查: get(int index):按照下标获取元素; 通用方法 getFirst():获取第一个元素; 特有方法 getLast():获取最后一个元素; 特有方法 peek():获取第一个元素,但是不移除; 特有方法 peekFirst():获取第一个元素,但是不移除; peekLast():获取最后一个元素,但是不移除; pollFirst():查询并删除头; 特有方法 pollLast():删除尾; 特有方法 poll():查询并移除第一个元素 特有方法