java数据结构之LinkedList
一、LinkedList源码注释
- //LinkedList源码 jdk版本1.8.0_121
- public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- {
- transient int size = 0;
- /**
- * 指向第一节点
- * first和last要么都为null,要么都不为null。不要把节点node和节点的item混淆
- */
- transient Node<E> first;
- /**
- * 指向最后一个节点
- */
- transient Node<E> last;
- /**
- * 构造一个空的LinkedList
- */
- public LinkedList() {
- }
- /**
- * 通过集合来构建LinkedList
- */
- public LinkedList(Collection<? extends E> c) {
- this();
- addAll(c);
- }
- /**
- * 将元素放在第一个位置
- * 具体做法就是创建一个新的节点newNode,newNode的item设置为该元素,然后newNode的next指向原来的第一个节点
- * 将first指向新的节点,如果是第一次添加节点就将last也指向该节点,如果不是第一次添加就将原来的第一个节点的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;
- size++;
- modCount++;
- }
- /**
- * 将元素放在最后一个位置
- * 创建一个新的节点newNode,将newNode的prev指向原来的最后一个节点
- * 将last指向newNode,如果是第一次添加就将first也指向newNode,如果不是第一次添加就将原来最后一个节点的next指向newNode
- */
- 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++;
- }
- /**
- * 将元素插入到另外一个节点的前面
- * 根据元素创建一个新的节点newNode,newNode的prev指向另一个节点succ的prev所指向的节点,newNode的next指向succ
- * 如果succ的prev为null,也就是说我succ为第一个节点,那么要将fisrt指向newNode,如果succ不是第一个节点,那么就让succ的prev所指向的节点的next指向newNode
- */
- 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++;
- }
- /**
- * 将节点从第一个位置上移除
- * 将第一个元素的item和next都置为null,方便垃圾回收
- * first指向将next指向的节点,如果next指向的节点为null,就将last也设置为null(也就是说这个要移除的节点是链表中的最后一个节点)
- * 如果next指向的节点不为null,那么就将next指向的节点的prev设置为null
- */
- 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;
- }
- /**
- * 移除最后一个元素
- * 将最后一个元素的item和prev置为null,它的last本来就为null所以不用管
- * 然后将last指向prev指向的节点,如果prev指向的节点为null,那么就将first也设置为null(也就是说这个要移除的节点是链表中的最后一个节点)
- * 如果prev指向的节点不为null,那么就将prev指向节点的next设置为null
- */
- 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;
- }
- /**
- * 移除一个节点
- * 如果该节点是第一个节点,就将first指向该节点的下一个节点,否则将该节点的上一个节点的next指向该节点的下一个节点
- * 如果该节点是最后一个节点,就将last指向该节点的上一个节点,否则就将下一个节点的prev指向该节点的上一个节点
- * 将当前的接电点的prev、item、next都置为null,方便GC
- */
- 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) {
- first = next;
- } else {
- prev.next = next;
- x.prev = null;
- }
- if (next == null) {
- last = prev;
- } else {
- next.prev = prev;
- x.next = null;
- }
- x.item = null;
- size--;
- modCount++;
- return element;
- }
- /**
- * 获取list中的第一个元素,直接通过first节点,获取其中的item,如果是空list会报错
- */
- public E getFirst() {
- final Node<E> f = first;
- if (f == null)
- throw new NoSuchElementException();
- return f.item;
- }
- /**
- * 获取list中的最后一个元素,直接通过last来获取,如果是空list会报错
- */
- public E getLast() {
- final Node<E> l = last;
- if (l == null)
- throw new NoSuchElementException();
- return l.item;
- }
- /**
- * 删除第一个节点,如果是空list会报错
- */
- public E removeFirst() {
- final Node<E> f = first;
- if (f == null)
- throw new NoSuchElementException();
- return unlinkFirst(f);
- }
- /**
- * 删除最后一个节点,如果是空list会报错
- */
- public E removeLast() {
- final Node<E> l = last;
- if (l == null)
- throw new NoSuchElementException();
- return unlinkLast(l);
- }
- /**
- * 将元素加入到list最前面
- */
- public void addFirst(E e) {
- linkFirst(e);
- }
- /**
- * 将元素加入到list最后面
- */
- public void addLast(E e) {
- linkLast(e);
- }
- /**
- * 判断list是否包含该对象
- */
- public boolean contains(Object o) {
- return indexOf(o) != -1;
- }
- /**
- * 返回元素的个数
- */
- public int size() {
- return size;
- }
- /**
- * 将元素添加到LinkedList尾部
- */
- 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;
- }
- /**
- * 批量加入集合中的元素到list尾部
- */
- public boolean addAll(Collection<? extends E> c) {
- return addAll(size, c);
- }
- /**
- * 批量加入集合中的元素到list的index处
- */
- public boolean addAll(int index, Collection<? extends E> c) {
- checkPositionIndex(index);
- //如果传入的集合中元素个数为0,直接返回false
- Object[] a = c.toArray();
- int numNew = a.length;
- if (numNew == 0)
- return false;
- Node<E> pred, succ;//pred 是指向插入的第一个元素的前一个节点,succ是指向插入的 最后一个元素的后一个节点
- if (index == size) {//如果index为size那就说明是插在最后一个元素的后面
- succ = null;
- pred = last;
- } else {
- succ = node(index);
- pred = succ.prev;
- }
- //循环将集合中的元素加入到list中
- 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;
- }
- //如果succ为null,则将last指向最后添加的元素,如果不为null则和加入的最后一个元素关联起来
- if (succ == null) {
- last = pred;
- } else {
- pred.next = succ;
- succ.prev = pred;
- }
- size += numNew;
- modCount++;
- return true;
- }
- /**
- * 清空LinkedList中所有的数据
- * 这里循环清空了所有节点之间的联系,虽然这不是必须的,但是这样做会有助于分代GC
- */
- 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++;
- }
- // Positional Access Operations 位置相关的一些操作,也就是和下标index有关
- /**
- * 获取某个下标处的元素
- */
- public E get(int index) {
- checkElementIndex(index);
- return node(index).item;
- }
- /**
- * 设置某个下标处的元素
- */
- public E set(int index, E element) {
- checkElementIndex(index);
- Node<E> x = node(index);
- E oldVal = x.item;
- x.item = element;
- return oldVal;
- }
- /**
- * 在某个下标处添加一个元素
- */
- public void add(int index, E element) {
- checkPositionIndex(index);
- if (index == size)//添加在最后面
- linkLast(element);
- else
- linkBefore(element, node(index));
- }
- /**
- * 删除某个下标处的元素
- */
- public E remove(int index) {
- checkElementIndex(index);
- return unlink(node(index));
- }
- /**
- * 判断下标处有没有元素
- */
- private boolean isElementIndex(int index) {
- return index >= 0 && index < size;
- }
- /**
- * 判断这个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处非null的节点,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;
- }
- }
- // Search Operations
- /**
- * 查找该对象在list中的下标,第一次出现的下标,没有返回-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;
- }
- /**
- * 返回对象在list中最后一次出现的下标
- */
- 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.队列的相关操作,先进先出
- /**
- * 返回list第一个元素,没有元素不会报错
- */
- public E peek() {
- final Node<E> f = first;
- return (f == null) ? null : f.item;
- }
- /**
- * 返回list中第一个元素,list为空会报错
- */
- public E element() {
- return getFirst();
- }
- /**
- * 返回并删除list中第一个元素,list为空不会报错
- */
- public E poll() {
- final Node<E> f = first;
- return (f == null) ? null : unlinkFirst(f);
- }
- /**
- * 删除并返回第一个list中的元素,list为空会报错
- */
- public E remove() {
- return removeFirst();
- }
- /**
- * 新增一个元素到list尾部
- */
- public boolean offer(E e) {
- return add(e);
- }
- //Queue operations end
- // Deque operations 双端队列的 一些操作
- /**
- * 在队列的 最前端加入一个元素
- */
- public boolean offerFirst(E e) {
- addFirst(e);
- return true;
- }
- /**
- * 在队列的 最后端加入一个元素
- */
- public boolean offerLast(E e) {
- addLast(e);
- return true;
- }
- /**
- * 获取队列的第一个元素
- */
- 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 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);
- }
- // Deque operations end
- //stack operations 出栈和入栈的 一些相关操作。
- /**
- * 向栈中压入一个元素
- */
- public void push(E e) {
- addFirst(e);
- }
- /**
- * 从栈中弹出一个元素,栈为空会报错
- */
- public E pop() {
- return removeFirst();
- }
- //stack operations end
- /**
- * 删除该对象第一次出现的节点
- */
- 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;
- }
- /**
- * 获取list迭代器
- */
- 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();
- }
- }
- //节点类,静态内部类,用来作为LinkedList内部的节点
- 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;
- }
- }
- /**
- * 逆向迭代器,只能从后往前迭代
- * @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")
- private LinkedList<E> superClone() {
- try {
- return (LinkedList<E>) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new InternalError(e);
- }
- }
- /**
- * 浅克隆方法
- */
- 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;
- }
- /**
- * 得到一个包含list所有元素的数组,长度为size
- */
- 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;
- }
- /**
- * 将list中的元素存到为数组a中,如果a的长度不否就从新反射构建一个长度为size的数组,如果长度有多的,则就后面的都设置为null
- */
- @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;
- /**
- * 将list实例保存到流中
- */
- 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);
- }
- /**
- * 从流中读取list实例
- */
- @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());
- }
- /**
- * 用来多线程并行迭代的迭代器,这个迭代器的主要作用就是把list分成了好几段,每个线程执行一段,因此是线程安全的。
- */
- @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;
- }
- }
- }
二、LinkedList源码分析
1、继承关系:通过下面的代码可以看出LinkedList本身继承了AbstractSequentialList,说明LinkedList比较适合于顺序访问。同时它实现了Deque接口,说明LinkedList也是双端队列的一个实现。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
2、内部结构
通过下面的代码可以看出LinkedList是基于node节点的双向链表,其内部定义了一个静态内部类Node来保存存入的数据,每次保存一个数据都会新建一个Node实例来进行存储。Node中item指向存入的数据,prev指向前一个节点,next指向后一个节点,如此节点之间连接起来构成一个双向链表,并不是循环链表。LinkedList类中定义了两个属性first和last分别用来指向第一个和最后一个节点,first和last要么都为null,要么都不为null。
//节点类,静态内部类,用来作为LinkedList内部的节点
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;
}
} /**
* 指向第一节点
* first和last要么都为null,要么都不为null。不要把节点node和节点的item混淆
*/
transient Node<E> first; /**
* 指向最后一个节点
*/
transient Node<E> last;
3、LinkedList与队列、双端队列和栈
通过LinkedList 实现的接口Deque可知,LinkedList实现了队列和双端队列,同样栈的相关方法也表示在了接口Deque中。
3.1、队列:有些队列是有长度的限制的,而LinkedList实现的队列没有长度限制
add和offer 都是向队列尾部添加元素,但是add在容量不够的时候报错(LinkedList没有容量限制所以不会报错),而offer则会返回false。
peek和element都是返回队列的第一个元素,如果没有元素peek返回null,element报错。但是二者在返回元素后不会将队列中的元素删除。
poll和remove 都是返回队列中的第一个元素并且将该元素从队列中删除,如果没有元素poll返回null,remove则会报错。
/**
* 将元素添加到LinkedList尾部
*/
public boolean add(E e) {
linkLast(e);
return true;
} /**
* 新增一个元素到list尾部
*/
public boolean offer(E e) {
return add(e);
} /**
* 返回list第一个元素,没有元素不会报错
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
} /**
* 返回list中第一个元素,list为空会报错
*/
public E element() {
return getFirst();
} /**
* 返回并删除list中第一个元素,list为空不会报错
*/
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
} /**
* 删除并返回第一个list中的元素,list为空会报错
*/
public E remove() {
return removeFirst();
}
3.2、双端队列:其实双端队列是在非双端队列的扩展,普通队列只能从尾部添加元素,从头部获取元素。双端队列是在两端都可以获取和添加元素。
/**
* 在队列的 最前端加入一个元素
*/
public boolean offerFirst(E e) {
addFirst(e);
return true;
} /**
* 在队列的 最后端加入一个元素
*/
public boolean offerLast(E e) {
addLast(e);
return true;
} /**
* 获取队列的第一个元素
*/
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 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);
}
3.3、栈
栈只能是出栈和入栈,都是从头部添加和获取元素。
/**
* 返回list第一个元素,没有元素不会报错
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
} /**
* 向栈中压入一个元素
*/
public void push(E e) {
addFirst(e);
} /**
* 从栈中弹出一个元素,栈为空会报错
*/
public E pop() {
return removeFirst();
}
三、LinkedList总结
1、LinkedList是通过Node节点保存存入的元素,并通过prev和next指向前一个节点和后一个节点来实现双向链表。
2、由于LinkedList是通过链表存储数据,所以查询某个下标的元素需要从头或者末尾开始查找,不能够随机快速访问,所以会慢一点。
3、在新增数据都末尾的时候由于不需要进行扩容操作,所以比较快速。但是如果想要在某个下标处新增元素需要先遍历到该下标处获取节点后才能新增。
4、由于实现了Deque,所以LinkedList也是队列、双端队列和栈的实现。
java数据结构之LinkedList的更多相关文章
- Java数据结构之LinkedList、ArrayList的效率分析
前言: 在我们平常开发中难免会用到List集合来存储数据,一般都会选择ArrayList和LinkedList,以前只是大致知道ArrayList查询效率高LinkedList插入删除效率高,今天来实 ...
- Java数据结构漫谈-LinkedList
同样是List的数据结构,LinkedList是使用了前后指针,指明节点的方式来表示链表的,这与之前介绍的ArrayList http://www.cnblogs.com/yakovchang/p/j ...
- Java集合之LinkedList
一.LinkedList概述 1.初识LinkedList 上一篇中讲解了ArrayList,本篇文章讲解一下LinkedList的实现. LinkedList是基于链表实现的,所以先讲解一下什么是链 ...
- JAVA数据结构系列 栈
java数据结构系列之栈 手写栈 1.利用链表做出栈,因为栈的特殊,插入删除操作都是在栈顶进行,链表不用担心栈的长度,所以链表再合适不过了,非常好用,不过它在插入和删除元素的时候,速度比数组栈慢,因为 ...
- Java数据结构之线性表(2)
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
- Java数据结构之线性表
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
- java 数据结构 图
以下内容主要来自大话数据结构之中,部分内容参考互联网中其他前辈的博客,主要是在自己理解的基础上进行记录. 图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示 ...
- 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现
本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型 栈是 ...
- Java数据结构和算法 - OverView
Q: 为什么要学习数据结构与算法? A: 如果说Java语言是自动档轿车,C语言就是手动档吉普.数据结构呢?是变速箱的工作原理.你完全可以不知道变速箱怎样工作,就把自动档的车子从1档开到4档,而且未必 ...
随机推荐
- Python3学习笔记37-LeetCode刷题
LeetCode中国官网一个用来刷编程题的网站,收录了很多面试题.感觉还是学习到很多.记录一下思路.代码还是要多敲. 建议编写完后直接在LeetCode上运行和提交.提交时会有不同的测试用例来测试代码 ...
- 关于C++编译时内链接和外链接
最近在阅读<大规模C++ 程序设计> 在第1部分,作者讨论了内链接和外链接问题(因为大规模的C++程序有繁多的类和单元.因此编译速度是个大问题) 这里记录一下关于内链接和外链接的理解. ...
- Vue项目中的文件导出
项目中涉及到文件导出,分xml和excel导出.不同的文件导出格式不同,需要根据文件类型判断导出格式. exportAllData(val){ //全部导出 if(!val){ this.export ...
- LVM卷管理
一.LVM是做什么的 LVM ( Logical Volume Manager ,逻辑卷管理器)LVM 是建立在磁盘和分区之上的一个逻辑层,用来提高磁盘分区管理的灵活性.LVM 可以对磁盘分区按照组的 ...
- Solving Docker permission denied while trying to connect to the Docker daemon socket
The error message tells you that your current user can’t access the docker engine, because you’re la ...
- k8s部署普罗米修斯
参考链接:https://blog.csdn.net/ywq935/article/details/80818390 https://www.jianshu.com/p/ac8853927528 1. ...
- WHU个人赛第二场C——前缀和&&后缀和
题目 链接 题意:给定 $n$ 个整数,去掉其中一个数使得剩下数字的gcd最大,求最大的gcd.($3 \leq n \leq 100000$) 分析 枚举每一个位置,显然每次枚举都计算所有数的gcd ...
- spring @Primary-在spring中的使用(转)
在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的.但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary ...
- 【git】git中使用https和ssh协议的区别以及它们的用法
git可以使用四种主要的协议来传输资料: 本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 git 协议.其中,本地协议由于目前大都是进行远程开发和共享代码所以一般不常 ...
- PHP mysqli_get_charset() 函数
mysqli_get_charset() 函数返回字符集对象. <?php $con=mysqli_connect("localhost","my_user&quo ...