LinkedList简介:
LinkedList 是实现了 List接口
和 Deque接口
的双向链表 . LinkedList 底层的链表结构使他支持高效的插入(尾插)和删除操作,此外它实现了Deque接口,使得LinkedList也具有队列的特性;LinkedList不是线程安全的
.
如果想使LinkedList 变成线程安全的,可以调用静态类 Collections类
中的synchronizedList
方法
List list = Collections.synchronizedList(new LinkedList(...));
内部结构图示 :
LinkedList源码分析:
1. 构造方法
空构造方法
public LinkedList() {
}
通过已有集合构造链表
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
2. add()方法
add(E e )方法: 将 元素添加到链表尾部
public boolean add(E e) {
linkLast(e);
return true;
}
调用了 linkLast(e) 方法
void linkLast(E e) {
final Node<E> l = last; //保存当前链表的尾节点
final Node<E> newNode = new Node<>(l, e, null); //创建一个需要添加的元素节点,将last作为它的pre
last = newNode; // 将新节点作为last
if (l == null)
first = newNode;// 当链表中没有节点
else
l.next = newNode;
size++;
modCount++;
}
add(int index ,E element) : 在指定位置添加元素
public void add(int index, E element) {
checkPositionIndex(index); //检查给定 index 是否有效,处于[0 ~ index ]之间
if (index == size)
linkLast(element); // 添加到链表尾部
else
linkBefore(element, node(index)); // 在链表中间插入
}
linkBefore(element, node(index))传入参数为 要添加的元素
和指定位置的Node节点
void linkBefore(E e, Node<E> succ) {
//保存传入节点的pre节点
final Node<E> pred = succ.prev;
//创建新的节点
final Node<E> newNode = new Node<>(pred, e, succ);
//将传入节点的pre设为新节点
succ.prev = newNode;
//判断pred节点为空情况,也就是传入节点为头结点
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
addAll(int index ,Collection C) : 将集合添加到链表指定位置
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); //判断index 是否在 [0~size] 之间
//用object数组保存传入集合元素
Object[] a = c.toArray();
int numNew = a.length;
//传入集合数组大小为0,返回false
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last; //传入index == size ,只用pred保存last节点
} else {
succ = node(index); // succ保存传入index位置的节点
pred = succ.prev; // pred 保存succ的pre节点
}
for (Object o : a) { //遍历插入数组元素
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
// 判断pred 为空的情况,也就是succ为头结点的情况
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
// 如果插入位置在尾部,重置last节点
if (succ == null) {
last = pred;
// 否则将插入的链表与先前链表连接起来(只做一次)
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
addFirst(E e ) : 将链表添加到头部
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) { //
final Node<E> f = first; //保存first 节点
final Node<E> newNode = new Node<>(null, e, f); // 创建
first = newNode;
// 判断f 节点是否为空
if (f == null)
last = newNode; // 尾节点设为新节点
else
f.prev = newNode; //不为空设置f节点的pre为新节点
size++;
modCount++;
}
3. 根据位置取数据的方法
get(int index):根据指定索引返回数据
public E get(int index) {
//检查index范围是否在size之内
checkElementIndex(index);
//调用Node(index)去找到index对应的node然后返回它的值
return node(index).item;
}
获取头结点(index = 0 ) 方法
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E element() {
return getFirst();
}
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
区别:getFirst() ,element()在链表为空时,抛出异常.
peek(),peekFirst()链表为空时返回null
常见问题 :
ArrayList 与 LinkedList 的区别 ?
是否保证线程安全
,ArrayList 和LinkedList都是不同步的,不保证线程安全底层数据结构
,ArrayList底层使用的是Object数组存储,LinkedList底层用的是双向链表(1.7之前为双向循环链表,1.7以及1.7之后为双向链表)插入删除时间复杂度
ArrayList的尾插时间效率高于LinkedList ,虽然LinkedList不需要扩容,但是需要new 新节点效率低. LinkedList更适合随机插入或者删除 ,因为ArrayList的随机插入和删除会带来移动数组的困扰.随机访问
ArrayList 支持随机访问(即通过元素序号得到元素对象) ,LinkedList 不支持随机访问内存空间占用
ArrayList 空间浪费在预留一定的空间容量,LinkedList的空间花费每个元素消耗比ArrayList更多的空间.