LinkedList源码详解(基于jdk1.8.0_231)
1. LinkedList简介
- LinkedList数据结构为双链表, 继承了AbstractSequentialList 实现了List, Deque, Cloneable, java.io.Serializable接口;
- 允许所有元素为null;
- LinkedList多线程环境下,不是线程安全的,需要外部加锁或使用其他线程安全的链表替代或使用Collettions.synchronizedList(new LinkedList(...))包装;
- 与ArrayList一样,Iterator,listIteratot两种迭代器都有fail-fast机制,提醒业务开发程序员,目前可能是多线程环境,使用LinkedList不安全;
- 与ArrayList不同,LinkedList由于是双端链表,一般地,LinkedList头尾插入和删除时间复杂为O(1),查找的时间复杂度为O(n),而ArrayList的头插入和删除时间复杂度为O(n),查找时间为O(1),所以在大量删除插入操作中推荐使用LinkedList,大量查找操作中推荐使用ArrayList.
- 相比于ArrayList,LinkedList在并行操作时,会有一些操作要求,ArrayList很容易并行.
- LinkedList没有所谓的扩容机制,不考虑虚拟内存情况下,内存余量多大,他可扩多大;
- 根据“实现了啥接口,就必须提供啥服务”原则,LinkedList实现Deque接口,则LinkedList一定可以做双端队列,当然也可以当普通队列啦。
- LinkedList实现了大量相似的方法,如增加节点 add addLast offer offerLast 这些方法都是将节点插入LinkedList尾部,我习惯于根据LinkedList实际场合的扮演的数据结构,使用对应的方法,或根据具体情况需要啥返回值采用对应的API。具体来讲,当LinkedList当单向链表,我习惯使用add添加元素,当linkedList当双向链表,我习惯使用addLast addFist添加元素,当LinkedList当单向队列,我习惯使用offer方法,当LinkedList当双向队列,我习惯使用offerLast offerFirst方法。在阅读完源码后,会给出相关的总结。“是啥数据结构,提供啥语义接口”这样我们的代码可读性才比较好。
2. LinkedList逻辑内存模型
根据LinkedList源码,我们称包含pre item next三个域的结构为一个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;
}
}
3. LinkedList UML简图
4. LinkedList API简介
自定义的字段
transient int size = 0;
transient Node<E> first; //头指针(对头结点的引用)
transient Node<E> last; //尾指针(对尾结点的引用)
private static final long serialVersionUID = 876323262645176354L;
直接继承的字段
protected transient int modCount = 0; //继承自AbstractList
构造函数
public LinkedList()
public LinkedList(Collection<? extends E> c)
override或新增的API(public 方法)
-----------------增加节点的方法---------------------
void addFirst(E e) //头插法,插入的新元素为头结点
void addLast(E e) //尾插法,插入的新元素为尾节点
boolean add(E e) //尾插法,插入的新元素为尾节点
void add(int index, E element)
boolean addAll(Collection<? extends E> c)
boolean addAll(int index,Collection<? extends E> c)
-----------------删除节点的方法---------------------
E remove(int index)
E removeFirst() //删除头结点
E removeLast() //删除尾节点
boolean remove(Object o) //按元素删除
boolean removeFirstOccurrence(Object o)
boolean removeLastOccurrence(Object o)
void clean()
----------------查找节点的方法----------------------
int indexOf(Object o)
int lastIndexOf(Object o)
E getFirst() //查头结点
E getLast() //查尾节点
E get(int index)
boolean contains(Object o)
int size()
----------------修改节点的方法----------------------
E set(int index,E element)
----------------LinkedList作为栈的方法--------------
void push(E e)
E pop()
---------------LinkedList作为双端队列的方法---------
boolean offerFirst(E e)
boolean offerLast(E e)
E pollFirst()
E pollLast()
E peekFirst()
E peekLast()
---------------- 作为单向队列的方法-----------------
boolean offer(E e) //等价于add
E poll()
E remove() //遇到队头为null,返回null
E peek()
E element() // 遇到对头为null,抛异常
---------------迭代器------------------------------
ListIterator<E> listIterator(int index)
Iterator<E> descendingIterator()
----------------LinkedList数组化------------------
Object[] toArray()
<T> T[] toArray(T[] a)
----------------其他方法--------------------------
Object clone() //LinkedList浅拷贝
Spliterator<E> spliterator()
直接继承超类或来自实现接口的default方法
public int hashCode() //继承自AbstractList
public boolean equals(Object o) //继承自AbstractList
public List<E> subList(int fromIndex, int toIndex)//继承自AbstractList
default Stream<E> stream()//来自Colletion接口
default Stream<E> parallelStream() //来自Colletion接口
default boolean removeIf(Predicate<? super E> filter) //来自接口Collection
public boolean isEmpty() //继承自AbstractCollection
public boolean retainAll(Collection<?> c) //继承自AbstractColletion
public Iterator<E> iterator() //继承自AbstractSequentialList
default void sort(Comparator<? super E> c) //来自List接口
default void replaceAll(UnaryOperator<E> operator) //来自List接口
default void replaceAll(UnaryOperator<E> operator) //来自List接口
LinkedList类的包方法
void linkLast(E e)
void linkBefore(E e,Node<E> succ)
E unlink(Node<E> x)
Node<E> node(int index)
LinkedList类的private方法
void linkFist(E e)
E unlinkFirst(Node<E> f)
E unlinkLast(Node<E> l)
boolean isElementIndex(int index)
boolean isPositionIndex(int index)
String outOfBounsMsg(int index)
void checkElementIndex(int index)
void checkPositionIndex(int index)
LinkedList<E> superClone()
void writeObject(java.io.ObjectOutputStream s)
void readObject(java.io.ObjectInputStream s)
5. LinkedList源码(基于jdk1.8.0_231)
package java.util;
import java.util.function.Consumer;
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; //尾结点的引用
//构造一个空的LinkedList
public LinkedList() {
}
//构造一个LinkedList包含具体的集合,如果集合为null,会抛出NullPointerException
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
//头插法,即每次添加的元素都为第一个元素,私有的类方法
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f); //
first = newNode; //更新first指向newNode,即newNode为头结点了
if (f == null) //f为空,即初始的链表为空,让first,last同时指向newNode
last = newNode;
else
f.prev = newNode; //否则f不为空,让f.pre指向newNode
size++;
modCount++;
}
//尾插法,即每次添加元素都添加在链表的最后一个后面,包方法
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode; //更新最后一个节点为newNode
if (l == null) //如果l为空,说明链表是空链表
first = newNode; //让first,last同时指向newNode
else
l.next = newNode; //否则让l.next指向newNode
size++;
modCount++;
}
//按元素插入,在节点succ前插入e节点,succ不能为null节点,注意null节点与节点的item=null不是一个概念,可以在item=null前插入新节点哦
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);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
//删除头节点frist,在将LinkedList作为队列,双端队列,弹出头节点时使用,private方法被poll removeFirst pollFirst等public方法调用
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;
}
//删除尾节点last,在将LinkedList作为队列,双端队列,弹出尾节点时使用或栈式,弹出栈顶,private方法被removeLast pollLast等public方法调用
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;
}
//把节点x前后断开,把x节点从链表中移除
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next; //x节点前节点
final Node<E> prev = x.prev; //x节点后节点
if (prev == null) { //前节点为空,说明x节点就是first指向的节点,就是第一个节点
first = next;
} else {
prev.next = next; //x前节点不为空, 让前节点next域指向next节点
x.prev = null; //将x节点prev域置空
}
if (next == null) { //x后节点为空,说明x节点就是last指向的节点,就是最后一个节点
last = prev;
} else {
next.prev = prev; //x后节点prev域指向x前节点
x.next = null; //x节点的next域置空
}
x.item = null;
size--;
modCount++;
return element;
}
//返回第一个节点item域
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//返回最后一个节点item域
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
//remove第一个节点
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//remove最后一个节点
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//头插法加入一个节点,每次节点都成为第一个节点,first更新指向新加入的节点
public void addFirst(E e) {
linkFirst(e);
}
//尾插法加入一个节点,每次节点都成为最后一个节点,last更新指向新加入的节点
public void addLast(E e) {
linkLast(e);
}
//检查链表是否至少存在一个对象o,若存在,返回true,时间复杂度为O(n)
public boolean contains(Object o) {
return indexOf(o) != -1; //indexOf遍历链表,得出o的位置,若o不在链表中,返回-1,否则返回位置索引
}
//返回链表种总共有多少个元素
public int size() {
return size;
}
//尾插法,加入新元素,时间复杂度为O(1),此方法等价于addLast
public boolean add(E e) {
linkLast(e);
return true;
}
//remove链表中顺序第一次出现的指定的对象o,移除成功会返回true,否则返回false
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;
}
//从链表尾部新加入一个集合,c为null,会抛NullPointerException
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//从具体位置新加入一个新集合
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
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;
modCount++;
return true;
}
//清空链表
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++;
}
//根据位置,返回位置上的节点item域,其时间复杂度为O(n),千万不要再迭代for循环中用来取元素,这可是线上生产级别的重大事故,炒鱿鱼丢饭碗级别的事故
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//将index位置的元素替换为element,并且返回原节点的item域,同get一样,千万不要在for中迭代中使用set
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//按位置插入,在index中插入elemet,index位置有元素的话,element就插在其前
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//按位置删除,删除第index位置的节点,返回删除节点的item域
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
//检查index在不在合理的链表范围,如果size在[0,size)返回true,否则返回false
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//检查index的范围,在add操作情况下,index=size也是合理的
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
//越界异常信息提示
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//返回index位置的节点,其中这里做个优化,如果index小于size的一半,从first指向的节点(头节点)往后找,如果index大于size一半,从last指向的节点(尾接点)从后往前找
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;
}
}
// Search Operations
//从头结点前往后,返回对象o在链表中首次出现的位置
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在链表中首次出现的位置
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;
}
// Queue operations.
//peek是返回头结点的item域,如果头结点为null,则返回null
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//返回头结点的item域,但是头结点=null时,会抛NoSuchElementException
public E element() {
return getFirst();
}
//返回头结点,且删除头结点,如果头结点=null,也返回null
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//返回头结点,且删除头结点,如果头结点=null,会抛出NoSuchElementException
public E remove() {
return removeFirst();
}
//加入一个元素到链表尾部
public boolean offer(E e) {
return add(e);
}
// Deque operations 因为LinkedList实现了Deque接口,必然需要提供双端队列该有“服务”,可前后入队和出队
//从头入队
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//从尾入队
public boolean offerLast(E e) {
addLast(e);
return true;
}
//返回头节点item域,头节点=null 也返回,不抛异常
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//返回尾节点,尾节点=null,页返回,不抛异常
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
//返回并删除头节点,头结点=null,也返回,不抛异常
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//返回并删除尾节点,尾结点=null,也返回,不抛异常
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
//下面push pop 将LinkedList作为栈,链表头始终作为栈顶,
//将LinkedList作为栈,头作为栈顶,压入一个元素入栈
public void push(E e) {
addFirst(e);
}
//将LinkedList作为栈,头作为栈顶,将一个个元素弹出栈
public E pop() {
return removeFirst();
}
//从头结点往后,remove链表第一次出现的对象o,remove成功返回true,否则返回false
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
//从尾结点往后,remove链表第一次出现的对象o,remove成功返回true,否则返回false
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;
}
//listIterator是双端迭代器,即可以从前也可以往后,也可以从特定位置开始迭代,迭代过程运行 fail-fast机制,通过迭代器的增删本质上就是对原链表的操作,
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
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;
}
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;
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);
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();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
//每个节点的数据结构
private static class Node<E> {
E item; //item域
Node<E> next; //next域
Node<E> prev; //prev域
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
/**
* @since 1.6
*/
//从尾结点开始往前遍历节点
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
/**
* Adapter to provide descending iterators via ListItr.previous
*/
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
@SuppressWarnings("unchecked")
//浅拷贝LinkedList
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
//返回LinkedList实例的一个浅拷贝,言下之意为:item域如果为引用类型,拷贝的只是指向内容的引用,而不是内存内容,若item是基本类型,可以认为就是深拷贝了
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;
}
//
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;
}
//
@SuppressWarnings("unchecked")
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;
}
private static final long serialVersionUID = 876323262645176354L;
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 (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
@SuppressWarnings("unchecked")
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());
}
@Override
public Spliterator<E> spliterator() {
return new LLSpliterator<E>(this, -1, 0);
}
/** A customized variant of Spliterators.IteratorSpliterator */
static final class LLSpliterator<E> implements Spliterator<E> {
static final int BATCH_UNIT = 1 << 10; // batch array size increment
static final int MAX_BATCH = 1 << 25; // max batch array size;
final LinkedList<E> list; // null OK unless traversed
Node<E> current; // current node; null until initialized
int est; // size estimate; -1 until first needed
int expectedModCount; // initialized when est set
int batch; // batch size for splits
LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
this.list = list;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getEst() {
int s; // force initialization
final LinkedList<E> lst;
if ((s = est) < 0) {
if ((lst = list) == null)
s = est = 0;
else {
expectedModCount = lst.modCount;
current = lst.first;
s = est = lst.size;
}
}
return s;
}
public long estimateSize() { return (long) getEst(); }
public Spliterator<E> trySplit() {
Node<E> p;
int s = getEst();
if (s > 1 && (p = current) != null) {
int n = batch + BATCH_UNIT;
if (n > s)
n = s;
if (n > MAX_BATCH)
n = MAX_BATCH;
Object[] a = new Object[n];
int j = 0;
do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
current = p;
batch = j;
est = s - j;
return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
}
return null;
}
public void forEachRemaining(Consumer<? super E> action) {
Node<E> p; int n;
if (action == null) throw new NullPointerException();
if ((n = getEst()) > 0 && (p = current) != null) {
current = null;
est = 0;
do {
E e = p.item;
p = p.next;
action.accept(e);
} while (p != null && --n > 0);
}
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public boolean tryAdvance(Consumer<? super E> action) {
Node<E> p;
if (action == null) throw new NullPointerException();
if (getEst() > 0 && (p = current) != null) {
--est;
E e = p.item;
current = p.next;
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
}
API总结(一)
LinkedList由于数据结构的灵活性,设计了大量API,一些增删改查的API功能很相似。这里我们仅仅讨论public方法,下面给出对比:
- 增加节点的API
-----------------------------------------双向链表中的增加节点方法------------------------------------------------------------
public void addFirst(E e) //增加节点成为头节点,头插法,函数内部调用私有方法linkFirst(E e) 完成头插
public void addLast(E e) //增加节点称为尾节点,尾插法,函数内部调用私有方法linkLast(E e) 完成尾插
----------------------------------------单向链表中增加节点的方法-------------------------------------------------------------
public boolean add(E e) //尾插法,增加一个新结点,返回true,函数内部调用私有方法linkLast(E e)完成尾插
public void add(int index, E element) //在指定位置新增一个新节点,只适用LinkedLinked为单双向链表的,不符合作为队列,栈
----------------------------------------addAll方法用在单双链表中都可---------------------------------------------------------
public boolean addAll(Collection<? extends E> c) // 加入一个集合的元素,LinkedList作为单向链表,双向链表,单向队列,双向队列使用此方法都合适,作为栈的时候,不合适
public boolean addAll(int index, Collection<? extends E> c) //从指定位置加入一个集合的元素,LinkedList作为单向链表,双向链表,单向队列,双向队列使用此方法都合适,作为栈的时候,不合适
-----------------------------------------单向队列中增加节点的方法---------------------------------------------------------
public boolean offer(E e) //等价于add(E e)方法,offer内部也是调用add方法的,实现尾插
-----------------------------------------双向队列中增加节点的方法---------------------------------------------------------
public boolean offerFirst(E e) //offerFist函数内部调用addFirst(E e),始终返回true,完成头插,等价于addFirst
public boolean offerLast(E e) // offerLast函数内部调用addLast(E e),始终返回true,完成尾插,等价于addFirst
----------------------------------------栈中增加节点的方法---------------------------------------------------------------
public void push(E e) //将元素压入栈顶,LinkedList始终将头节点作为栈顶,push函数内部调用addFist函数完成操作,等价于addFist函数
- 删除节点的API
public E removeFirst() // 移除头结点,如果头节点为null,抛出NoSuchElementEXception,函数内部调用私有方法unlinkFirst(Node<E> f)完成头移除
public E removeLast() // 移除尾结点,如果尾节点为null,抛出NoSuchElementEXception,函数内部调用私用方法unlinkLast(Node<E> l)完成尾移除
public boolean remove(Object o) // 移除指定的节点o,节点o可以为null,若节点o存在在链表中,移除成果,返回true,否则返回false
public E remove(int index) //移除指定位置的元素,会检查index的范围[0,size)
public void clear() //清空LinkedList
-------------------------------------LinkedList作为单向队列时的删除方法-----------------------------------------------------------------
public E poll() //检索和删除对头节点,若对头为空(即队列为空),返回null,不为空,返回头节点的item域,poll函数内部调用私有unlinkFirst(Node<E> f)方法
public E remove() // 等价于removeFist函数,移除对头节点,如果头节点为null,抛出NoSuchElementEXception
-----------------------------------------双向队列中删除节点的方法-------------------------------------------------------------
public E pollFirst() //移除双端队列的队头节点,若队头节点为null,返回null,若不为空,移除对头,返回对头节点的item域
public E pollLast() //移除双端队列的队尾节点,若队尾节点为null,返回null,若不为空,移除对尾,返回对尾节点的item域
----------------------------------------栈中删除节点的方法---------------------------------------------------------------
public E pop() //弹出栈顶的元素,函数内部调用removeFirst()完成操作,等价于removeFirst
- 查询节点的API
-----------------------------------------单双链表中的查询节点方法------------------------------------------------------------
public E getFirst() // 得到头结点的item域,如果头结点为null,抛出NoSuchElementException
public E getLast() // 得到尾结点的item域,如果尾结点为null,抛出NoSuchElementException
public E get(int index) //得到指定index位置的元素,会检查index的范围[0,size)
public int indexOf(Object o) //顺序(从头结点往尾节点)查找节点o第一次出现在LinkedList中的位置
public int lastIndexOf(Object o) //逆序(从尾结点往头结点)查找节点o第一次出现在LinkedList中的位置
----------------------------------------单项队列中查询节点的方法-------------------------------------------------------------
public E peek() //返回队列的第一个节点,如队列为空,返回null,队列不为空,返回对头节点的item域,peek函数只负责检索,不会删除对头节点
public E element() //返回队列的第一个节点,如队列为空,抛出NoSuchElementException,函数内部调用getFist()函数,两者是等价的.element函数只负责检索,不会删除对头节点
-----------------------------------------双向队列中查询节点的方法---------------------------------------------------------
public E peekFirst() // 返回双端队列的队头,如果对头为null,返回null,如果队头不为空,返回队头节点的item域
public E peekLast() // 返回双端队列的队尾,如果对尾为null,返回null,如果队尾不为空,返回队尾节点的item域
- 更新节点的API
public E set(int index, E element) //将index位置的元素更新为element
6. LinkedList示例
作为双向链表使用
public class LinkedListTest {
public static void main(String[] args) {
List<Integer> arr = new Random(32)
.ints(0,1000).boxed().limit(20).collect(Collectors.toList());
System.out.println(arr);
/**
* 创建
*/
LinkedList<Integer> myLinkedList = new LinkedList<>(arr);
/**
* 作为双端列表 getFirst getLast removeFirst removeLast addFirst addLast set removeIf replaceAll
* indexOf lastIndexOf
*/
System.out.println(myLinkedList.getFirst());
System.out.println(myLinkedList.getLast());
myLinkedList.removeFirst();
myLinkedList.removeLast();
System.out.println(myLinkedList);
myLinkedList.addFirst(377); //头插
myLinkedList.addLast(644); //尾插
System.out.println(myLinkedList);
myLinkedList.set(myLinkedList.size() - 1,-1);
System.out.println(myLinkedList.get(myLinkedList.size()-1));
System.out.println(myLinkedList);
System.out.println(myLinkedList.set(myLinkedList.size()-1, 644));// 注意 get set 本身操作都是O(n)
System.out.println(myLinkedList);
myLinkedList.addAll(Arrays.asList(-1,-2,-3,-4));
System.out.println(myLinkedList);
myLinkedList.removeIf(x->x<=0);
System.out.println(myLinkedList);
myLinkedList.addAll(Arrays.asList(0,0,0,0));
System.out.println(myLinkedList);
myLinkedList.replaceAll(x -> {
if(x == 0) x =100;
return x;
});
System.out.println(myLinkedList);
myLinkedList.removeAll(Arrays.asList(100));
System.out.println(myLinkedList);
System.out.println(myLinkedList.indexOf(122));
System.out.println(myLinkedList.lastIndexOf(122));
/**
* 迭代操作
*/
System.out.println(myLinkedList);
ListIterator<Integer> listIterator = myLinkedList.listIterator();
listIterator.forEachRemaining(x-> System.out.print(x+","));
System.out.println();
myLinkedList.forEach(x-> System.out.print(x+","));
System.out.println();
Iterator<Integer> iterator = myLinkedList.descendingIterator();
iterator.forEachRemaining(x-> System.out.print(x+","));
System.out.println();
Iterator<Integer> ite = myLinkedList.iterator();
ite.forEachRemaining(x-> System.out.print(x+","));
System.out.println();
int size = myLinkedList.size();
for(int i =0;i<size;i++){
System.out.print(myLinkedList.get(i)+","); //不要这样做,这个时间复杂度是O(n*n)
}
}
}
- 结果
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
377
644
[331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
-1
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, -1]
-1
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, -1, -2, -3, -4]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, 0, 0, 0, 0]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, 100, 100, 100, 100]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
5
5
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
644,611,92,406,748,676,346,806,80,937,734,256,791,200,122,239,41,985,331,377,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
作为栈使用
练习使用push pop函数,LinkedList作为栈,头节点始终为栈顶
public class MyStack<E> extends LinkedList<E> {
public void push(E e){
super.push(e);
}
public E pop(){
E e = super.pop();
return e;
}
public static void main(String[] args) {
MyStack<String> stack = new MyStack<>();
stack.push("hello");
stack.push("world");
System.out.println(stack.toString());
String s = stack.pop();
System.out.println(s);
System.out.println(stack);
}
}
- 结果
[world, hello]
world
[hello]
作为双端队列使用
public class MyQueue<E> extends LinkedList<E> {
public MyQueue(Collection<? extends E> c) {
super.addAll(c);
}
/**
* 对头的增删查
*
*/
//peekFirst是返回头结点的item域,如果头结点为null,则返回null
@Override
public E peekFirst() {
return super.peekFirst();
}
//从队头入队
@Override
public boolean offerFirst(E e) {
return super.offerFirst(e);
}
//返回头结点,且删除头结点,如果头结点=null,也返回null
@Override
public E pollFirst() {
return super.pollFirst();
}
/**
* 队尾的增删查
*/
@Override
public boolean offerLast(E e) {
return super.offerLast(e);
}
@Override
public E removeLast() {
return super.removeLast();
}
@Override
public E peekLast() {
return super.peekLast();
}
public static void main(String[] args) {
MyQueue<Integer> myQueue = new MyQueue<>(Arrays.asList(20,13,15));
System.out.println(myQueue);
myQueue.offerLast(100);
System.out.println(myQueue); //加入队尾
myQueue.offerFirst(1);
System.out.println(myQueue); //加入队头
System.out.println(myQueue.peekFirst());
System.out.println(myQueue.peekLast());
myQueue.offerLast(null);
System.out.println(myQueue.peekLast()); //返回null
myQueue.pollFirst();
System.out.println(myQueue); //从对头出队
myQueue.pollLast();
System.out.println(myQueue);//从队尾出队
}
}
- 结果
[20, 13, 15]
[20, 13, 15, 100]
[1, 20, 13, 15, 100]
1
100
null
[20, 13, 15, 100, null]
[20, 13, 15, 100]
7. 面试session
-
谈谈你对LinkeList的认识?
LinkedList数据结构是双端链表,由于其数据结构的灵活性,LinkedList可以做单向链表,双向链表,单向队列,双端队列,栈(头结点所在位置始终为栈顶)。针对不同的数据结构,LinkedList给出了操作API.
当LinkedList做单向链表时,我习惯使用add方法
当LinkedList做双端链表时,我习惯使用getFirst getLast removeFirst removeLast addFirst addLast方法
而 get set addALL 这些方法一般,在单双向链表中,我都会使用.
当LinkedList做单向队列时,我习惯使用 offer remove() poll peek element方法,其中peek element都是返回队头,当peek遇到空队头时,return null,而element抛异常;
当做双端队列时,我习惯使用peekFirst peekLast offerFirst offerLast pollFirst pollLast
当做栈时,我习惯使用pop push方法
一般地,peek返回值,不会删除;offer一般等价add;peek,poll在遇到null都会返回null,不会抛异常。 -
LinkedList和ArrayList的异同和使用场景?
简单来讲,大量的删除 增加 适合LinkedList,大量的随机访问适合ArrayList.特别低,大量在头部的增加删除操作,特别适合LinkedList,大量中间位置的查找适合ArrayList. -
LinkedList可以模拟哪些数据结构?
单向链表 双端链表 栈 队列 双端队列 -
LinkedList有几种迭代方式?
forEach listIterator() iterator() descendingIterator()