- 双向链表由若干节点构成,其中每个节点均由3部分构成(前驱节点prev,元素本身ele,后继节点next),各节点在内存空间中物理地址不连续;保存元素均为引用类型,元素位置实际保存的是对象对应的地址,基本类型元素包装存入即可;
应用:LinkedList
操作:
- 增删元素 boolean add(E e)
在链表尾部添加元素 boolean add(E e)
void linkLast(E e) {
//定义一个节点指向尾节点last
final Node<E> l = last;
//新建一个节点,前驱节点为l,元素为e,后继节点为null
final Node<E> newNode = new Node<>(l, e, null);
//使newNode节点为尾节点
last = newNode;
//判断l是否为空,即是否存在头节点
if (l == null)
//头节点不存在,则头尾节点均指向newNode节点
first = newNode;
else
//头节点存在,则使前一节点的尾节点指向newNode节点
l.next = newNode;
size++;//链表长度
modCount++;//链表被修改次数
}
在链表之间添加元素(除首尾)void add(int ele,E e)
void linkBefore(E e, Node<E> succ) {
// assert succ != null
final Node<E> pred = succ.prev;
//将插入元素封装为节点,前驱节点为pred 后继节点指向下一节点
final Node<E> newNode = new Node<>(pred, e, succ);
//下一节点的前驱节点指向新建节点
succ.prev = newNode;
if (pred == null)
first = newNode;
else
//使前一节点的后继节点指向新建节点
pred.next = newNode;
size++;
modCount++;
}
- 删除元素 E remove(int index)
E unlink(Node<E> x) {
// assert x != null;
//要删除的元素
final E element = x.item;
//删除元素的下一节点
final Node<E> next = x.next;
//删除元素的上一节点
final Node<E> prev = x.prev;
if (prev == null) {
//prev为空 为首节点 将头指针指向下一节点
first = next;
} else {
//使前一节点的后继节点指向下一节点
prev.next = next;
//使被删除元素前驱节点为空
x.prev = null;
}
if (next == null) {
//next为空 为尾节点 将头指针指向下一节点
last = prev;
} else {
//使后一节点的前驱节点指向前一节点
next.prev = prev;
//使被删除元素后继节点为空
x.next = null;
}
//使被删除元素为空
x.item = null;
//链表长度-1
size--;
//操作次数+1
modCount++;
//返回被删除元素
return element;
}
- 查找元素 E get(int index)
遵循对半查找,若查找元素下标大于链表长度一半,则从尾节点开始逆序查找;若查找元素下标小于链表长度一半,则从头结点开始顺序查找,从而提高查询效率。双向链表查询速度大于单向链表原因就在于对半查找。
//>>右移一位 相当于size/2
//判断查找元素下标是否小于链表长度一半
if (index < (size >> 1)) {
//小于 从头节点开始查找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//大于 从尾节点开始查找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
- 修改元素 E set(int index,E e)
//检查下标是否越界
checkElementIndex(index);
//获取到下标对应元素
Node<E> x = node(index);
//保存对应元素的旧值
E oldVal = x.item;
//将修改值赋给对应元素
x.item = element;
return oldVal;
ArrayList和LinkedList的区别
ArrayList底层由动态数组实现,随机查询速度较快,在内存中存储空间连续,只需要通过起始地址和偏移量查询即可,空间复杂度为O(1);元素增删速度较慢,每次增删操作都会影响数组中其他元素的存储位置,下标会改变,所以速度较慢;
LinkedList底层由双向链表实现,随即查询速度较慢,在内存空间中存储位置不连续,只能从头尾节点依次遍历链表中元素;但元素增删速度较快,只需要在对应位置将链表断开,再为前驱节点、后继节点重新赋值即可;尤其在首尾增删元素速度会更快。