标签:++ check 点数据 说明 ima ali 数组实现 rac 增删改
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
可以看到LinkedList类继承AbstractSequentialList类,实现了List, Deque, Cloneable, java.io.Serializable接口。实现List接口,实现对列表的增删改查操作,并且元素可以为null,实现Deque接口,为add,poll提供先进先出队列操作,以及其他堆栈和双端队列操作。LinkedList是不同步的,多线程不安全,迭代器是快速失败的(fail-fast),不能再迭代过程中对集合进行修改操作。
//集合的元素个数
transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;
LinkedList的成员变量很简单只有三个集合:size元素个数,first头节点,last尾节点。
空参构造方法:
public LinkedList() {
}
传入一个集合:
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
可以看到调用了addAll()方法将传入集合的所有元素添加到了LinkedList中。
private void linkFirst(E e) {
//保存当前集合的头节点到f
final Node<E> f = first;
//e保存到newNode中,关于 Node<E>的源码分析在后面
final Node<E> newNode = new Node<>(null, e, f);
//头节点赋为newNode
first = newNode;
//判断原集合是否为空(判断原头节点是否为null)
if (f == null)
//如果原集合为空,将集合的last尾节点也赋为newNode
last = newNode;
else
//如果原集合不为空(first不为null),将原头节点的前驱节点赋为newNode
f.prev = newNode;
//结合长度自加
size++;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
}
void linkLast(E e) {
//保存当前集合的尾节点到l
final Node<E> l = last;
//e保存到newNode中
final Node<E> newNode = new Node<>(l, e, null);
//尾节点赋为newNode
last = newNode;
//判断尾节点是否为null(集合是否为空)
if (l == null)
//如果集合是空,头节点赋为newNode first=last
first = newNode;
else
//集合不为空,将新的节点追加到原集合的last尾节点后作为新的尾节点
l.next = newNode;
//集合个数自加
size++;
//对集合做出修改modCount自加保证集合版本一致
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的前驱节点赋为新节点
succ.prev = newNode;
//判断succ即要插入的位置的节点的前驱节点是否为null
if (pred == null)
//如果为空那么原节点就是头节点,所以将新节点置为头节点
first = newNode;
else
//如果不为空,succ不是集合的头节点,那么将前驱节点的后继节点置为新节点
pred.next = newNode;
//集合个数自加
size++;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
}
private E unlinkFirst(Node<E> f) {
//取出头节点的值存到element中
final E element = f.item;
//将头节点的后继节点存到next中
final Node<E> next = f.next;
//将头节点的值和下个节点的地址值置为null
f.item = null;
f.next = null; // help GC
//原头节点的后继节点置为集合的头节点
first = next;
//判断原头节点的后继节点是否为空
if (next == null)
//如果为空,那么原集合就只有一个节点,那么尾节点置为空
last = null;
else
//不为空,将新的头节点的前驱节点置为null
next.prev = null;
//集合长度自减
size--;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
//返回原头节点保存的数据
return element;
}
private E unlinkLast(Node<E> l) {
//取出位节点保存的数据
final E element = l.item;
//取出尾节点的前驱节点
final Node<E> prev = l.prev;
//尾节点保存数据置为null
l.item = null;
//尾节点的前驱节点置为null
l.prev = null; // help GC
//原尾节点的前驱节点置为当前结合的尾节点
last = prev;
//判断原尾节点的前驱节点是否为null
if (prev == null)
//如果是那么头节点置为null,集合为空
first = null;
else
//否,那么原尾节点的前驱节点的后继节点置为null
prev.next = null;
//集合长度自减
size--;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
//返回原尾节点保存的数据
return element;
}
E unlink(Node<E> x) {
//取出要移除节点保存的数据
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;
//移除节点的后继节点置为null
x.prev = null;
}
//移除节点的后继节点为null即要移除的节点为集合的尾节点
if (next == null) {
//将移除节点的前驱节点值为集合的尾节点
last = prev;
} else {
//将原节点的前驱节点置为后继节点的前驱节点
next.prev = prev;
//移除节点的后继节点置为null
x.next = null;
}
//移除节点保存的数据置为null
x.item = null;
//集合长度自减
size--;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
//返回移除节点保存的数据
return element;
}
private boolean isElementIndex(int index) {
//判断index是否在0到size之间 包括0 但是不包括size
return index >= 0 && index < size;
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
Node<E> node(int index) {
//判断传入的index是否在集合的前半部分
if (index < (size >> 1)) {
Node<E> x = first;
//从0开始遍历到到指定的index处
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//如果index在集合的后半部分
Node<E> x = last;
//倒着遍历集合
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
尽管知道索引index,但是由于LinkedList没有实现RandomAccess接口,所以不能依靠索引快速查询,只能通过遍历的方法,node通过先判断index是在前半部分还是在后半部分,将遍历的时间复杂度从O(n)降低到了O(n/2)但是对于数据庞大的集合,这样的查找效率还是太低了。
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 boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int size() {
return size;
}
public int indexOf(Object o) {
//index初始化index
int index = 0;
//如果传入o为null
if (o == null) {
//遍历集合找到为村川数据为null的元素返回其索引
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
//遍历集合找到存储数据和传入的o相同的元素返回其索引
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
//没有找到返回-1
return -1;
}
##### 5.6 lastIndexOf(Object o)返回指定数据最后出现的索引
public int lastIndexOf(Object o) {
//倒着遍历,所以初始化index为size
int index = size;
//如果穿的数据为null,倒着遍历找到数据为null的节点
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
//倒着遍历找到数据为o的节点
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
//没有找到返回-1
return -1;
}
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E element() {
return getFirst();
}
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
public E removeFirst() {
//取出头节点
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
//直接调用unlikFirst方法删除头节点
return unlinkFirst(f);
}
public E removeLast() {
//取出尾节点
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
//调用unlinkLast方法删除尾节点
return unlinkLast(l);
}
public void addFirst(E e) {
//直接调用linkFirst方法添加头节点
linkFirst(e);
}
public void addLast(E e) {
//调用linkLast添加节点
linkLast(e);
}
public boolean add(E e) {
//调用linkLast方法在更换尾节点
linkLast(e);
return true;
}
public boolean remove(Object o) {
//判断传入数据是否为null
if (o == null) {
//遍历集合
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
//找到元素,用unlink方法删除
unlink(x);
return true;
}
}
} else {
//遍历集合
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
//找到元素,用unlink方法删除
unlink(x);
return true;
}
}
}
//没有找到这个元素返回false
return false;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//检查下标是否越界
checkPositionIndex(index);
//将传入的c转化为数组
Object[] a = c.toArray();
//获取数组的长度
int numNew = a.length;
//如果数组为空返回false
if (numNew == 0)
return false;
//pred succ节点 index节点
Node<E> pred, succ;
//如果插入的索引位置为集合尾部
if (index == size) {
//succ节点置null
succ = null;
//将集合的尾节点赋给pred
pred = last;
} else {
//如果不是在尾部插入指定的集合,那么用node方法找到要插入位置的节点
//赋给succ
succ = node(index);
//pred就为succ的前驱节点
pred = succ.prev;
}
//遍历数组
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//数组的值放置到节点中newNode前驱节点为pred
Node<E> newNode = new Node<>(pred, e, null);
//如果前驱节点为null
if (pred == null)
//将插入的节点置为集合头节点
first = newNode;
else
//前驱节点不为null,将前驱节点的后继节点置为插入的节点
pred.next = newNode;
//pred置为插入的节点,进入下个循环
pred = newNode;
}
//succ是否为null,说明在结合尾部添加的所以尾节点就是pred
if (succ == null) {
//将最后的新节点置为尾节点
last = pred;
} else {
//否则是在集合中插入的 那么前驱节点的后继节点就是succ succ是index处的节点
pred.next = succ;
//插入的最后的一个节点为succ的前驱节点
succ.prev = pred;
}
//集合个数增加插入节点个数
size += numNew;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
return true;
}
public void clear() {
//遍历所有节点将所有节点前驱节点,保存数据,后继节点都置为null
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
//头节点和尾节点置为null
first = last = null;
//集合长度为0
size = 0;
//对集合做出修改modCount自加保证集合版本一致
modCount++;
}
public E get(int index) {
//检查index是否在0到size范围
checkElementIndex(index);
//调用node方法获取index处的节点的数据
return node(index).item;
}
public E set(int index, E element) {
//检查index是否在0到size范围
checkElementIndex(index);
//取得index处的节点
Node<E> x = node(index);
//取得原index处节点的数据
E oldVal = x.item;
//index处的节点数据置为element
x.item = element;
//将index处的原数据返回
return oldVal;
}
public void add(int index, E element) {
//检查index是否在0到size范围内
checkPositionIndex(index);
//如果index等于size,那么在集合尾添加
if (index == size)
linkLast(element);
else
//调用linkBefore方法,指定位置前添加节点
linkBefore(element, node(index));
}
public E remove(int index) {
//检查index是否在0到size范围内
checkElementIndex(index);
//调用unLink方法解除一个节点
return unlink(node(index));
}
public E poll() {
final Node<E> f = first;
//判断头节点是否为null,如果为null的话直接返回null,不是的话调用nulinkFirst方法删除并返回头节点
return (f == null) ? null : unlinkFirst(f);
}
public E remove() {
return removeFirst();
}
public boolean offer(E e) {
return add(e);
}
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
public boolean removeLastOccurrence(Object o) {
//当要删除的元素为null
if (o == null) {
//从尾节点向前遍历集合
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
//调用nulink方法删除节点
unlink(x);
return true;
}
}
} else {
//从后向前遍历集合
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
//没有找到这个元素返回false
return false;
}
LinkedList底层是通过双向链表来实现的
LinkedList线程不安全。
对于双向链表来说增删效率很高,对于单向链表删除某个节点还需要从头遍历来说,效率高很多,对于要操作的节点直接使用unlink()方法就可以实现删除。而对于查找来说,由于LinkedList没有索引,所以无法快速获取某个节点,必须要遍历,尽管遍历的时候已经可以降低到O(N/2),但是相比数组直接通过索引快速获取来说还是效率低了很多。因此,需要经常插入增删的数据采用LinkedList保存更好。
关于ArrayList的源码分析可以看这里。
标签:++ check 点数据 说明 ima ali 数组实现 rac 增删改
原文地址:https://www.cnblogs.com/Linteresting/p/10776365.html