Linked
参考:
掌握知识
- 数据结构
- LinkedList的基本属性
- Linked的构造器
- 添加元素 add、addAll、set
- 删除元素 remove、clear
- 获取元素 get
- 遍历LinkedList
- 判断元素是否存在 contains
- peek、poll、push操作
- LinkedList的元素排序
数据结构
1、LinkedList是基于双端双向链表实现的,可以当作是栈、队列、双端队列
2、实现了List接口,是顺序容器,继承AbstractList的迭代器
3、允许NULL值
4、非线程安全
5、重写了克隆、系列化、反系列化
ArrayList的基本属性
- first:Node
- last:Node
- size:ArrayList中元素的实际个数
- modCount:记录LinkedList中修改次数,用于并发时抛出异常
transient Node first;
first指针指向链表的第一个节点,用于从首部操作链表,Node是LinkedList中实际存储的节点对象
private static class Node<E> {
E item;//当前节点存储的值
Node<E> next;//记录前一个节点
Node<E> prev;//记录后一个节点
//构造函数
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}d
transient Node last;
last指针指向链表的最后一个节点,用于从尾作链表
private int size;
链表中存储的Node的实际个数
protected transient int modCount = 0;
LinkedList构造器
提供了无参的构造器和持有一个集合的构造器
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
//调用addAll方法将元素节点添加到链表中
addAll(c);
}
jdk.18之前的LinkedList还持有一个空的Header,1.8将LinkedList换成双端双向链表了,不再有header节点了。
//1.6的LinkedList
private transient Entry<E> header = new Entry<E>(null, null, null);
public LinkedList() {
header.next = header.previous = header;
}
添加元素 add、addAll、set
向链表中添加元素,找到插入位置的前一个和后一个元素,然后移动指针
//添加到链表最后
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
//检查下标是否越界 index >= 0 && index <= size
checkPositionIndex(index);
//如果插入位置是最后,调用插入到最后的方法
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//在尾部添加
void linkLast(E e) {
final Node<E> l = last;
//最后一个的next为NULL
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
//在指定节点前插入
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
//获取指定位置元素
Node<E> node(int index) {
//如果大于一半则从后往前遍历
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;
}
}
//addAll
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
//插入位置的后一个节点
succ = node(index);
/插入位置的前一个节点
pred = succ.prev;
}
//遍历集合将每个元素依次加入链表
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//指定前一个元素
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
//指定后一个元素
pred.next = newNode;
pred = newNode;
}
//处理尾部
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
//set就是查找到指定位置的元素,然后更改item
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
删除元素 remove、clear
从链表中删除元素,移动待删除元素的前面一个节点和后面一个节点的指针,最后将删除的元素的指针指向NULL,gc工作。
public E remove(int index) {
checkElementIndex(index);
return unlink(node(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) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
//置空,让gc回收
x.item = null;
//长度-1
size--;
modCount++;
return element;
}
//清空 就是将所有的元素都置为空
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
获取元素 get
根据位置获取指定位置的元素值
public E get(int index) {
checkElementIndex(index);
//查找到指定位置元素 然后获取item
return node(index).item;
}
遍历LinkedList
继承 AbstractSequentialList中的 迭代器,双端链表,可前可后
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public E previous() {
checkForComodification();
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
判断元素是否存在 contains
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
peek、poll、push操作
peek 返回链表中第一个元素但不移除,poll移除链表中第一个元素并返回
// 返回链表中第一个元素
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//移除链表中第一个元素并返回
public E poll() {
final Node<E> f = first;
eturn (f == null) ? null : unlinkFirst(f);
}
//向链表首部插入元素
public void push(E e) {
addFirst(e);
}
LinkedList的元素排序
总结
- LinkedList 基于双端双向两边实现,无容量的限制(会扩容)
- LinkedList 插入、删除元素比较快,如果只要调整指针的指向那么时间复杂度是 O(1) ,但是如果针对特定位置需要遍历时,时间复杂度是 O(n) 。