哈喽,大家好,今天我们来简单聊聊LinkedList
LinkedList是由双链表组成的集合,它不是线程安全的,如果有在多线程中添加或删除一个或多个元素,需要自己做同步处理,也可以调用List list = Collections.synchronizedList(new LinkedList(...));来获取一个线程同步的集合。
下面我们开始简单分析一下源码,首先来看看LinkedList这个类实现了哪些关键的接口。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
我们可以从源码中看出LinkedList实现了List接口来方便集合操作,同时也实现了Deque接口,这样就有了许多操作链表的方法。
Node
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;
}
}
有三个参数,item是当前元素的值,next指的是下一个元素,prev指的是上一个元素。
重要参数
transient Node<E> first;
transient Node<E> last;
在LinkedList中有两个比较重要的参数,一个是first,指的是链表中第一个元素。而last指的就是链表中最后一个元素。
LinkedList()
public LinkedList() {
}
这个LinkedList的一个空构造函数,没有做任何其他操作。
LinkedList(Collection<? extends E> c)
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
这是一个带参数的构造函数,将传入进来的集合全部添加到了LinkedList中,addAll这个方法我们在后面进行讲解。
getFirst()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
这个方法是获取第一个元素的值,这里是直接将first赋值给f,因为first在LinkedList中就是指的第一个元素。如果f为null的话就会报错。最后会返回f的值。
getLast()
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
这个方法是获取最后一个元素的值,这个方法和getFirst()方法类似,直接将last赋值给l,最后返回l的值。
removeFirst()
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
private E unlinkFirst(Node<E> f) {
//将f的值赋值给element。
final E element = f.item;
//将f的下一个元素赋值给next。
final Node<E> next = f.next;
//将f的值和f中的next都设置为null,方便GC
f.item = null;
f.next = null; // help GC
//将first设置为f的next
first = next;
//如果next为null,证明链表中只有一个元素,那么将最后一个元素也设置为null。
if (next == null)
last = null;
else
//如果不为null,那么将next的prev设置为null,原本指向的是f。
next.prev = null;
size--;
modCount++;
//最后返回f的值。
return element;
}
这个方法是移除第一个元素。还是先将first赋值给f,然后调用unlinkFirst方法并将f传入。unlinkFirst的相关解释写在了代码中。
removeLast()
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
这个方法和removeFirst方法逻辑差不多,是将最后一个元素置为null,然后将倒数第二个元素赋值给last。
addFirst(E e)
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
//将first赋值给f。
final Node<E> f = first;
//根据传入的值e,new一个新的Node出来,并将f传入,设置为新Node的下一个元素。
final Node<E> newNode = new Node<>(null, e, f);
//将第一个元素设置为新的Node。
first = newNode;
//如果f为null表示原本链表中没有元素,这时候添加了一个元
//素后第一个,最后一个元素都是newNode,所以要设置last为newNode。
if (f == null)
last = newNode;
else
//否则将原本第一个元素的prev设置为newNode。
f.prev = newNode;
size++;
modCount++;
}
在链表的头部添加一个元素,关键解释放在了代码中。
addLast(E e)
public void addLast(E e) {
linkLast(e);
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
addLast和addFirst大致逻辑差不多。只是将元素加在了链表最后,并重新设置了last的值。
add(E e)
public boolean add(E e) {
linkLast(e);
return true;
}
add(E e)方法和addLast(E e)相比只是多了一个返回值。
remove(Object o)
public boolean remove(Object o) {
//先判断传入的o是否为null。
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
//如果为null,则用==来比较。
if (x.item == null) {
//调用unlink方法来删除元素。
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
//如果不为null,则用equals来比较值。
if (o.equals(x.item)) {
//调用unlink方法来删除元素。
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
// assert x != null;
//将要删除元素x的item,next,prev分别提出来。
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
//如果x的prev为null,证明x为第一个元素,删除x后,这里就将next设置为第一个元素。
first = next;
} else {
//如果不为空,将x元素上一个元素的next指向x的next。
prev.next = next;
x.prev = null;
}
if (next == null) {
//如果x的next为null,说明x为最后一个元素,设置last为x的上一个元素。
last = prev;
} else {
//如果不为空,将x的下一个元素的prev指向x的prev。
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
remove(Object o)方法的关键解释已经放在了代码中。
addAll(Collection<? extends E> c)
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//先检查传入的index是否越界,因为这个的index默认为size,所以会将c直接添加到链表末尾。
checkPositionIndex(index);
//将c转为数组,并判断c的长度,如果为0,说明c为空数组,直接返回false。
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
//如果index等于size,说明将添加到链表末尾,所以pred为last。
succ = null;
pred = last;
} else {
//如果index不等于size,说明将添加到链表中,将目前index的元素赋值给succ,
//并将上一个元素赋值给pred。
succ = node(index);
pred = succ.prev;
}
//通过for循环生成新的Node实例,然后将每一个新的Node以此添加到pred后面。
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;
}
//添加完成后如果succ为null,则为在链表末尾添加,我们将我们添加的最后一个元素设置为last。
//如果succ不为null,则将原本index的下一个元素添加到prev后面。
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
addAll(Collection<? extends E> c)方法的关键解释放在了代码中。
clear()
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++;
}
clear()方法很简单,通过for循环依次将元素设置为null。
get(int index)
public E get(int index) {
//检查index是否越界。
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
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;
}
}
在get(int index)方法中,会先判断index是在链表的前一半还是在后一半,然后分别从第一个元素或者是最后一个元素来看是向前或向后查找index对应的元素。这样作会使查找速度更快。
set(int index, E element)
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
这里直接调用node方法获取index对应的元素,然后将元素的值替换为element。
add(int index, E element)
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
//如果index为size则将element放入新的Node中并添加到链表末尾。
linkLast(element);
else
//否则将element放入新的Node中,添加到index对应元素前面。
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
//获取index对应元素的前一个元素。
final Node<E> pred = succ.prev;
//新的Node位置在index对应元素和前一个元素之间。
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
add(int index, E element)方法的解释已经放在了代码中。
remove(int index)
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
remove(int index)方法先检查index是否越界,然后调用node方法获取index对应的元素,最后调用unlink方法删除这个元素。
indexOf
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;
}
先判断传入的o是否为null,如果为null则在for循环中用==来比较,如果不为null,则用equals来比较值,最后返回元素对应的index,如果没有对应的元素,则返回-1。
toArray()
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
toArray()方法先生成一个相同size的数组,然后通过for循环将链表的值全部赋值给数组,最后返回数组。
到这里LinkedList的一些基本方法就分析完成了,代码并不是很复杂,我们通过分析源码可以发现LinkedList在增加,删除方面代码很简单,相对应的速度也就比较快。在查找,修改方面的代码就比较复杂,每次都会从头开始去找相应的元素,速度也就会比较慢。综合来看如果你的需求中有大量的增加,删除的话可以考虑使用LinkedList。
如果文中有错误的地方欢迎大家指出来,也可以留言交流。