文章目录
上一篇文章
LinkedList是基于链表实现的,物理存储空间不连续,插入删除简单,查找修改都需要进行遍历。
Node节点
双向链表
LinkedList的节点Node包含对象item 、指向上一个节点的prev 、指向下一个节点的next。
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;
}
}
LinkedList构造函数
空参构造函数,或者指定集合
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
transient int size = 0; //包含的节点个数
transient Node<E> first; //双向链表的头结点
transient Node<E> last; //双向链表的尾节点
//空的构造函数,在使用的时候才创建节点
public LinkedList() {}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); //判断index是否处于0-size之间
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; //size增加
modCount++;
return true;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//数据是否越界判断
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
add(Object) 链表尾部添加对象
add(E e) 在链表的尾部添加对象
- 创建一个新的节点newNode,newNode的prev指向以前的last尾指针
- newNode是新的尾节点last
- 如果以前的尾节点l为null,则以前就是个空链表,需要给first头指针赋值
- 如果以前的尾节点l不为null,非空链表,l的next指向新节点newNode
public boolean add(E e) {
linkLast(e); //默认在链表的尾部添加对象
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); //创建一个新的节点,prev指向以前的last尾节点
last = newNode; //新节点赋给last尾指针
if (l == null) //如果以前的last尾指针==null,则以前就是个空链表,需要给first头指针赋值
first = newNode;
else // 否则的话以前的尾指针的next指向新节点
l.next = newNode;
size++;
modCount++;
}
add(index, element) 指定位置添加元素
如果index == size,则直接在链表的尾部添加对象
在指定的index插入对象,并更新相应的节点prev和next引用指向
public void add(int index, E element) {
checkPositionIndex(index); //如果index<0或者index>size,则抛出IndexOutOfBoundsException
if (index == size)
//如果index == size,则直接在链表的尾部添加对象
linkLast(element);
else
linkBefore(element, node(index));
}
//返回指定index的节点,这里对链表进行了折半查找,提升了部分效率
Node<E> node(int index) {
if (index < (size >> 1)) { //如果index在前一半,从0-index进行遍历定位
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last; //如果index在后一半,从size-1到index 倒序遍历定位
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//在指定的index插入对象,
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
//创建新节点newNode,newNode.prev指向succ.prev, newNode.next指向succ
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode; //succ.prev指向newNode
if (pred == null) // 如果pred==null,succ则是以前的头结点,需要更新first;
first = newNode;
else //否则pred.next指向newNode
pred.next = newNode;
size++;
modCount++;
}
remove(index) 移除指定index的节点
移除指定index的节点,更新前后节点prev和next。
public E remove(int index) {
checkElementIndex(index);//如果index<0或者index>size,则抛出IndexOutOfBoundsException
return unlink(node(index));
}
E unlink(Node<E> x) { //x需要移除的节点
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) { //如果x是头结点,则直接更新头结点first
first = next;
} else {
prev.next = next; //否则x的上一个节点的next直接指向x.next
x.prev = null; // help GC
}
if (next == null) { //如果x是尾节点,则直接更新尾节点last
last = prev;
} else {
next.prev = prev; //否则x的下一个节点的prev直接指向x.prev
x.next = null; // help GC
}
x.item = null; // help GC
size--;
modCount++;
return element; //返回移除的元素值
}
removeFirst() & removeLast() 移除头结点、尾节点
移除头结点和尾节点,并释放相应的头结点和尾节点的空间
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null; // help GC
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null; // help GC
size--;
modCount++;
return element;
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null; // help GC
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null; // help GC
size--;
modCount++;
return element;
}
set(index, element) 修改指定索引的元素
node(index)通过index定位到节点x,将节点x.item改为element,并返回oldValue
不改变位置,只改变存储内容
public E set(int index, E element) {
checkElementIndex(index);//如果index<0或者index>size,则抛出IndexOutOfBoundsException
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
get(index) 查找指定索引的存储对象
node(index)通过index定位到节点x,返回该节点存储的对象。
public E get(int index) {
checkElementIndex(index);//如果index<0或者index>size,则抛出IndexOutOfBoundsException
return node(index).item;
}
contains(object)是否包含某个对象
是否包含某个对象,通过equals匹配item
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;
}
size()存储的对象总数
返回存储的对象总数
// Returns the number of elements in this list
public int size() {
return size;
}
总结
LinkedList是基于双向链表实现的,物理存储空间不连续,插入删除简单,只需要修改上下节点的prev/next指向,查找修改需要先进行遍历定位,然后修改返回。