java集合LinkedList
基于jdk_1.8.0
关于List,主要是有序的可重复的数据结构。jdk主要实现类有ArrayList(底层使用数组)、LinkedList(底层使用双向链表)
LinkedList:
(一)继承关系图
(二)源码分析
1. 关键字段
/**
* 当前链表元素个数
*/
transient int size = 0; /**
* 指向第一个节点的指针
*/
transient Node<E> first; /**
* 指向最后一个节点的指针
*/
transient Node<E> last;
2. 构造方法
public LinkedList() {
} public LinkedList(Collection<? extends E> c) {
this();
addAll(c); //详见 3. public boolean addAll(Collection<? extends E> c)
}
3. 常用方法
a. public boolean add(E e)
/**
* 向链表的尾部追加元素
* 等效于调用addLast方法,只不过addLast没有返回值
* @param e
* @return
*/
public boolean add(E e) {
linkLast(e);
return true;
} /**
* 链表尾部追加元素
* 第一个添加的元素节点作为链表的头结点
* @param e
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); // 构造新的node节点
last = newNode; //尾节点指向新节点
if (l == null)
first = newNode; //空表时,头节点也指向新节点
else
l.next = newNode; //将新节点添加到链表
size++;
modCount++;
} // LinkedList$Node.class
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;
}
}
b. public void add(int index, E element)
/**
* 将指定的元素插入到列表中的指定位置。
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException
*/
public void add(int index, E element) {
checkPositionIndex(index); if (index == size)
linkLast(element); // 详见 a. public void add(E element)
else
linkBefore(element, node(index));
} private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
} /**
* 在 succ节点前插入
* @param e
* @param succ
*/
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); // newNode.prev = pred; newNode.next = succ;
succ.prev = newNode; //更新succ的前继节点
if (pred == null)
first = newNode; // 在第一个元素节点前插入,更新头节点
else
pred.next = newNode; //将新节点加入链表
size++;
modCount++;
} /**
* 最坏的情况需要遍历size/2个节点找到 index位置的节点
* @param index
* @return
*/
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;
}
}
c. public boolean addAll(Collection<? extends E> c)
/**
* 将指定集合中的所有元素追加到链表
* 默认尾插
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c); //详见 4. public boolean addAll(int index, Collection<? extends E> c)
}
d. public boolean addAll(int index, Collection<? extends E> c)
/**
* 从指定位置开始插入,性能不如尾插
* 虽然说,链表的随机插入较快,但是node(int index) 最坏也要遍历size/2个节点才能找到该位置的节点
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); // index < 0 || index > size throws IndexOutOfBoundsException 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); // 获取index位置的节点
pred = succ.prev; // 待插入节点的前继节点
} for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null); // newNode.prev = pred; newNode.next = null;
if (pred == null) //空表更新头节点
first = newNode;
else
pred.next = newNode; //更新前继节点的next指向当前新节点
pred = newNode;
} if (succ == null) { // 空表更新尾节点
last = pred;
} else {
pred.next = succ; // 更新最后添加新节点的后继节点为原index位置节点
succ.prev = pred; // 更新原index位置节点的前继节点为最后新添加元素的节点
} size += numNew;
modCount++;
return true;
}
e. public void addFirst(E e) // override for Deque
/**
* 头插法,对应的就是尾插法 addLast(E e)
* 头插,尾插性能想同,不过默认的add(E e)使用的是尾插法
* @param e
*/
public void addFirst(E e) {
linkFirst(e);
} 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;
size++;
modCount++;
}
f. public void addLast(E e) // override for Deque
/**
* 尾插法,对应的头插法 addFirst(E e),性能想同
* @param 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++;
}
g. public E remove() // override for Deque
/**
* 从链表中移除并返回第一个元素。
* @return
*/
public E remove() {
return removeFirst();
} /**
* 从链表中移除并返回第一个元素。
*
* @return the first element from this list
* @throws NoSuchElementException if this list is empty
*/
public E removeFirst() {
final Node<E> f = first; // 很多地方都采用了局部变量
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
} /**
* 该方法并没有判断参数是否null并且为第一个节点 需要调用者去保证
* 不过不理解的地方是,明明有first成员变量,为什么还要当做参数传进来
* @param f 头节点
* @return 移除掉的元素
*/
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
h. public E remove(int index)
/**
* 移除列表中指定位置的元素
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
} /**
* 从链表中移除节点x
* @param x
* @return x.item
*/
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) { // x为第一个节点
first = next;
} else {
prev.next = next; // 把待移除节点的前继节点的next指向待移除节点的next
x.prev = null;
} if (next == null) { // x为最后一个节点
last = prev;
} else {
next.prev = prev; // 把待移除节点的后继节点的prev指向待移除节点的prev
x.next = null;
} x.item = null;
size--;
modCount++;
return element;
}
i. public boolean remove(Object o)
/**
* 删除第一个存在链表中的 o
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x); //移除x节点 详情 h. public E remove(int index)
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x); //移除x节点 详情 h. public E remove(int index)
return true;
}
}
}
return false;
}
j. public E set(int index, E element)
/**
* 更新指定位置的元素
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
checkElementIndex(index); // index < 0 || index >= size throws IndexOutOfBoundsException
Node<E> x = node(index); // 获取index位置的节点
E oldVal = x.item;
x.item = element;
return oldVal;
}
k. public E get(int index)
/**
* 获取链表指定位置的元素
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index); // index < 0 || index >= size throws IndexOutOfBoundsException
return node(index).item;
}
l. public int indexOf(Object o)
/**
* 查找链表中元素o所在的第一个下标
*
* @param o element to search for
* @return the index of the first occurrence of the specified element in
* this list, or -1 if this list does not contain the element
*/
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)) // 需要重写实体类的equals方法
return index;
index++;
}
}
return -1;
}
m. public int lastIndexOf(Object o)
/**
* 从后往前遍历查找元素o所在的位置
*
* @param o element to search for
* @return the index of the last occurrence of the specified element in
* this list, or -1 if this list does not contain the element
*/
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;
}
n. public Iterator<E> iterator() //AbstractSequentialList.class
// LinkedList ——> AbstractSequentialList ——> AbstractList // AbstractSequentialList.class
public Iterator<E> iterator() {
return listIterator(); // AbstractList.listIterator()
} public abstract ListIterator<E> listIterator(int index); // AbstractList.class
public ListIterator<E> listIterator() {
return listIterator(0); // LinkedList.listIterator(0)
} // LinkedList.class
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
} // LinkedList$ListItr.class
private class ListItr implements ListIterator<E> {
/**
* 主要是提供给 remove、set方法用,在调用remove、set方法前 务必首先调用next() 或者 previous,否则lastReturned为null 会抛IllegalStateException
* 也要注意调用remove、add方法后lastReturned会置为null
*/
private Node<E> lastReturned; // next() 或者 previous()返回的最新节点
private Node<E> next; // 当前游标节点
private int nextIndex; // 当前游标位置
private int expectedModCount = modCount; ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
} public boolean hasNext() {
return nextIndex < size;
} public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException(); lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
} public boolean hasPrevious() {
return nextIndex > 0; //第一个节点没有前继节点
} /**
* 调用previous()要小心默认 listIterator() {
* listIterator(0); // 此种情况下使用hasPrevious()遍历,返回的永远都是false
* }
* 对应next() 应调用listIterator(list.size())
* @return
*/
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev; // 从后往前遍历
nextIndex--;
return lastReturned.item;
} public int nextIndex() {
return nextIndex;
} public int previousIndex() {
return nextIndex - 1;
} public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException(); Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null; // 防止再次remove
expectedModCount++;
} public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
} public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e); // 尾插
else
linkBefore(e, next); // 在next节点前插入e
nextIndex++;
expectedModCount++;
} public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
} /**
* 防止使用iterator遍历的时候,还操作list
*/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
o. public ListIterator<E> listIterator(int index)
/**
* 指定从index位置开始遍历链表
* 如果使用previous ,必须调用该方法,指定index > 0
* @param index
* @return
*/
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index); //参考 n. public Iterator<E> iterator()
}
p. public Object[] 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;
}
q. public <T> T[] toArray(T[] a)
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item; if (a.length > size)
a[size] = null; return a;
}
r. 其他基本方法
/**
* 清空列表
*/
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
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++;
} /**
* 克隆列表 ,需要实体类自行重写Object.clone()方法,才可实现深度复制
* @return
*/
public Object clone() {
LinkedList<E> clone = superClone(); // Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0; // Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item); return clone;
} /**
* 列表是否包含元素o
* @param o
* @return
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
} /**
* 当前列表节点数量
* @return
*/
public int size() {
return size;
} // 序列化相关
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject(); // Read in size
int size = s.readInt(); // Read in all elements in the proper order.
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
} private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject(); // Write out size
s.writeInt(size); // Write out all elements in the proper order.
for (LinkedList.Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
s. Deque方法
public void addFirst(E e) {
linkFirst(e);
} public void addLast(E e) {
linkLast(e);
} public E element() {
return getFirst();
} 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 peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
} 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 poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
} 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 E pop() {
return removeFirst();
} public void push(E e) {
addFirst(e);
} public E remove() {
return removeFirst();
} 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 boolean removeFirstOccurrence(Object o) {
return remove(o);
} public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
写在最后:
通过构造方法,还有add方法,可以看出,没有构造没有存储任何元素的头结点,第一个存储的元素作为链表的头结点;
通过Node内部类prev,next可以看出LinkedList是双向链表,既可以从尾部向头部遍历,也可以从头部向尾部遍历;
非必要情况下,尽可能不要调用按位置插入方法,会多一步按位置查找节点影响性能。对于插入时选择头插法还是尾插法,性能都是一样的。默认add(E e) 调用的是addLast;
通过对比ArrayList,发现LinkedList除了实现了AbstractList还实现了Deque,可以把LinkedList当做队列、栈操作,比如addFirst、addLast、remove、pop、push等;
使用listIterator.hasPrevious()从后往前遍历的时候,应注意list.listIterator(list.size());
LinkedList理论上只要内存足够,几乎没有最大元素上限;
和ArrayList一样,LinkedList也是非线程安全的,存放其中的实体,同样也需要重写Object.equals()方法。如果需要调用list.clone(),还应重写Object.clone()方法;
LinkedList,在随机插入由于不需要移动元素,故比ArrayList要快,但在get(int index)、set(int index, E e)随机访问要比ArrayList慢
java集合LinkedList的更多相关文章
- Java 集合 LinkedList的ListIterator
Java 集合 LinkedList的ListIterator @author ixenos 摘要:ListIterator<E>是继承自Iterator<E>的接口.list ...
- 6.Java集合-LinkedList实现原理及源码分析
Java中LinkedList的部分源码(本文针对1.7的源码) LinkedList的基本结构 jdk1.7之后,node节点取代了 entry ,带来的变化是,将1.6中的环形结构优化为了直线型链 ...
- Java集合---LinkedList源码解析
一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...
- Java集合 LinkedList的原理及使用
Java集合 LinkedList的原理及使用 LinkedList和ArrayList一样是集合List的实现类,虽然较之ArrayList,其使用场景并不多,但同样有用到的时候,那么接下来,我们来 ...
- Java集合-LinkedList源码分析
目录 1.数据结构-链表 2.ArrayList结构特性 3.构造方法 4.成员变量 5.常用的成员方法 6.Node节点 7.序列化原理 8.迭代器 9.总结 1.数据结构-链表 链表(Linked ...
- java集合-LinkedList
一.概述 LinkedList 与 ArrayList 一样实现 List 接口,只是 ArrayList 是 List 接口的大小可变数组的实现,LinkedList 是 List 接口链表的实现. ...
- Java 集合 - LinkedList
一.源码解析 (1). 属性 // 链表长度 transient int size = 0; // 链首和链尾 transient Node<E> first; transient Nod ...
- Java集合——LinkedList源码详解
)LinkedList直接继承于AbstractSequentialList,同时实现了List接口,也实现了Deque接口. AbstractSequentialList为顺序访问的数据存储结构提供 ...
- Java集合:LinkedList源码解析
Java集合---LinkedList源码解析 一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据re ...
随机推荐
- html5的文档申明为什么是<!DOCTYPE html>?
首先我们来了解一下什么是文档声明: 文档声明就是文档告诉游览器该以什么样的标准去解析它.游览器可以解析的文档可不止html,还有xhtml,xml...当然在这里我们并不需要知道xhtml.xml是什 ...
- 三、用Delphi10.3 创建一条JSON数据的第三种方法,非常简洁的写法
一.用Delphi10.3构造一个JSON数据的第三种方法,并格式化输出,代码如下: uses // System.JSON, System.JSON.Types, System.JSON.Write ...
- 26-[jQuery]-内容补充
jquery除了咱们上面讲解的常用知识点之外,还有jquery 插件.jqueryUI知识点 jqueryUI 官网: https://jqueryui.com/ jqueryUI 中文网: http ...
- Codeforces 914 C. Travelling Salesman and Special Numbers (数位DP)
题目链接:Travelling Salesman and Special Numbers 题意: 给出一个二进制数n,每次操作可以将这个数变为其二进制数位上所有1的和(3->2 ; 7-> ...
- thymeleaf多条件判断
解决办法:将逻辑关系全部写到大括号里面 <div th:if="${task.getStatusStr() !='已延期' ||task.getStatusStr()!='已完成'}& ...
- PLSQL Developer 客户端没有TNS监听,无法连接数据库
在Windows Server 2008 中安装了 64位的Oracle,好不容易将监听做好,在使用客户端 PLSQL Developer 的时候发现竟然没有TNS监听. 问题如下: 如上图所示,打开 ...
- 由 Session 和 Cookie 的区别说起
Session 和 Cookie 有什么区别? 最近面试被问到这个问题,和面试官一番讨论了解到面试官心里的答案后,我不太满意. 面对上面的问题,如果是刚毕业时的我,一定会毫不犹豫说出 Cookie 是 ...
- JavaScript——历史与简介
上一篇博文距离现在已经四个月了,一直想写些什么无奈工作比较忙碌.我的恩师老王在毕业聚餐那天带着一声酒气告诉我一定要把博客坚持写下去,所以今天下决心要开始这个新的篇章. 之所以想要从头写一个关于Java ...
- loadrunner11和https
最近做了一个接口测试的项目,json格式,https协议,使用postman调试这个接口,在postman中写好三个表头Authorization.sessionIndex.Content-Type和 ...
- 关于Netty的学习前总结
摘要 前段时间一直在学习netty因为工作忙的原因没有写一个学习的总结,今天抽个空先把总结写了吧.事先声明,本文不会详细的介绍每一个部分不过每个部分都会附上讲解详细的url.本文只是为了解释通Nett ...