刚入java不久的程序猿,对于简单的使用已毫不满足,最终为了一探究竟,翻开了JDK的源码,以下观点为自己的理解及看了多篇博客的总结,欢迎各位大神指出不对的地方,当然也欢迎和我一样刚学的同学,一起加油努力吧~~
LinkedList是什么 |
LinkedList与ArrayList一样,是一个存储数据的集合,LinkedList是由链表实现的,所以这也决定了其特性,增删快,查询慢,所以在查询多的情况下,我们应该选择ArrayList,增删多的情况下选择LinkedList
LinkedList源码解析 |
我想在日常的编码中,LinkedList应该也算比较常用的一个集合了,那么它具体是如何实现的呢,下面我们来一探究竟,首先看下一个整体的继承与实现体系吧
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
...
}
LinkedList继承了AbstractSequentialList,实现了List接口,Deque接口,Cloneable接口以及序列化的接口,这里我们可以看到实现了List接口,即可以使用集合的迭代,实现了双向队列,则可使用队列的相应方法,下面我们来看下AbstractSequentialList这个抽象类里写了些什么
public abstract class AbstractSequentialList<E> extends AbstractList<E> {
/**
* 无参构造,修饰符为protected,用于子类函数的调用
*/
protected AbstractSequentialList() {
}
/**
* 根据下标获得某个元素
*/
public E get(int index) {
try {
return listIterator(index).next();
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 在下标index处放入某个元素
*/
public E set(int index, E element) {
try {
ListIterator<E> e = listIterator(index);
E oldVal = e.next();
e.set(element);
return oldVal;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 在下标index处插入元素
*/
public void add(int index, E element) {
try {
listIterator(index).add(element);
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 移除下标为index的元素
*/
public E remove(int index) {
try {
ListIterator<E> e = listIterator(index);
E outCast = e.next();
e.remove();
return outCast;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 讲整个集合c添加到LinkedList中
*/
public boolean addAll(int index, Collection<? extends E> c) {
try {
boolean modified = false;
ListIterator<E> e1 = listIterator(index);
Iterator<? extends E> e2 = c.iterator();
while (e2.hasNext()) {
e1.add(e2.next());
modified = true;
}
return modified;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 将LinkedList转换为Iterator集合
*/
public Iterator<E> iterator() {
return listIterator();
}
/**
* 抽象方法由子类去实现,返回ListIterator集合
*/
public abstract ListIterator<E> listIterator(int index);
}
上面为AbstractSequentialList的源码,上面写了一些简单方法的具体作用,这里需要注意的是最后的抽象方法,由子类去实现,既然是子类实现,那么就进入LinkedList看看吧
/**
* 集合中元素的个数
*/
transient int size = 0;
/**
* 首节点的引用
*/
transient Node<E> first;
/**
* 尾节点的引用
*/
transient Node<E> last;
与往常一样,先了解下各个变量的作用,接下来看构造函数
/**
* 无参构造
*/
public LinkedList() {
}
/**
* 调用无参构造后,将集合中元素添加进LinkedList
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 调用addAll的重载方法,参数为元素数量与集合c
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 将集合内所有元素添加进LinkedList
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
//转换为数组
Object[] a = c.toArray();
int numNew = a.length;
//判断该数组是否为空,为空则不进行插入操作,返回false
if (numNew == 0)
return false;
Node<E> pred, succ;
//在结尾插入时,后续元素为null,前面元素为last,否则后续元素为node(index),前面元素为succ.prev
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;
}
/**
* 返回false时,抛出异常
*/
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* index在0到size之间时返回true
*/
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
/**
* 返回当前下标处的节点
*/
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
LinkedList构造函数只有这两个,addAll方法里我们可以看到元素是如何添加进去的,找到添加的位置将节点添加进去,对于首节点和尾节点做了相应的判断,由于每一个元素都是节点,所以都有上一个节点与下一个节点,LZ在这里理解也花费了些时间,刚开始还是有点晕的,理清了就还好了,下面我们看下Node这个内部类,了解下节点组成
//Node内部类
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;
}
}
上面我们可以看到,这个内部类里主要保存了上节点与下节点的引用与当前节点的数据,是一个很简单的类,下面我们来看一下几个比较重要的方法
/**
* 在链表表头插入节点
*/
private void linkFirst(E e) {
final Node<E> f = first;
//取代原来的头节点,在最前端插入
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//如果原先节点就不存在,则头尾都是同一个节点
if (f == null)
last = newNode;
else
//否则在原节点之前插入
f.prev = newNode;
//节点个数加1
size++;
modCount++;
}
/**
* 在链表尾部插入节点
*/
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;
//节点个数加1
size++;
modCount++;
}
/**
* 在某个节点之后插入节点
*/
void linkBefore(E e, Node<E> succ) {
// 传入节点不能为空,pred该节点的上一个节点
final Node<E> pred = succ.prev;
//新建节点,前节点为pred,后节点为succ
final Node<E> newNode = new Node<>(pred, e, succ);
//取代原先的前节点
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
/**
* 移除链表表头节点
*/
private E unlinkFirst(Node<E> f) {
// f不为空并且为头节点
final E element = f.item;
//f的下一个节点
final Node<E> next = f.next;
//头节点值置空,并将下一节点置空,等待GC回收
f.item = null;
f.next = null; // help GC
//f的下一个节点变为头节点
first = next;
if (next == null)
last = null;
else
next.prev = null;
//节点个数减1
size--;
modCount++;
return element;
}
/**
* 移除链表尾部节点
*/
private E unlinkLast(Node<E> l) {
// l不为空并且l为最后一个节点
final E element = l.item;
// l的上一个节点
final Node<E> prev = l.prev;
//尾节点值置空,并将上一节点置空,等待GC回收
l.item = null;
l.prev = null; // help GC
//l的上一个节点变为尾节点
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
//节点个数减1
size--;
modCount++;
return element;
}
/**
* 删除中间节点
*/
E unlink(Node<E> x) {
// 节点x不能为空
final E element = x.item;
//x的下一个节点
final Node<E> next = x.next;
//x的上一个节点
final Node<E> prev = x.prev;
//x的上一个节点为空,则x的下一个节点为首节点
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
//x的下一个节点为空,则x的上一个节点为尾节点
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
//节点个数减1
size--;
modCount++;
return element;
}
好了,主要的节点方法到这已经介绍完了,下面我简单的介绍一些其他的方法
/**
* 获取首节点元素,如该链表无元素则抛出异常
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 获取尾节点元素,如该链表无元素则抛出异常
*/
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
/**
* 获取首节点元素,如该链表无元素则抛出异常
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* 获取尾节点元素,如该链表无元素则抛出异常
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 在链表前端插入节点
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 在链表后端插入节点
*/
public void addLast(E e) {
linkLast(e);
}
/**
* 判断是否包含该元素
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
* 返回节点数量
*/
public int size() {
return size;
}
/**
* 添加节点(在尾部添加)
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* 移除某个节点
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
/**
* 清空链表,将首尾节点置空并讲节点数量置0
*/
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++;
}
/**
* 获得某个下标的节点值
*/
public E get(int index) {
//检查范围
checkElementIndex(index);
return node(index).item;
}
/**
* 设置下标为index处的节点值
*/
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
/**
* 在index处添加节点
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* 移除下标为index处的节点
*/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
/**
* 判断index处节点是否存在
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
/**
* 越界提示
*/
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
好了,上面只是简单的写了每个方法的作用,毕竟大多数都是调用上面详细介绍的几个增加删除节点的方法,源码后面还有些许就没列举了,都是一些重写的方法,没什么太多介绍的,LinkedList主要源码到这基本介绍完了,虽然说花了几天的时间啃这源码,但是收获还是挺多的