集合(二) LinkedList 详解

317 阅读6分钟

往期推荐

LinkedList概述

  • LinkedList是一个实现了List接口和Deque接口的双端链表
  • LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性;
  • LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:
List list=Collections.synchronizedList(new LinkedList(...));

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;

LinkedList内部类

private static class Node<E> {
    //存储的元素
    E item;
    //指向下一个结点的引用
    Node<E> next;
    //指向前一个结点的引用
    Node<E> prev;
    //Node<E>构造方法
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

LinkedList构造方法

//无参构造
public LinkedList() {}
//带有集合参数的构造器
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

LinkedList常用方法

添加

  • boolean add(E e) 将指定的元素追加到此列表的末尾。
public boolean add(E e) {
        linkLast(e);//这里就只调用了这一个方法
        return true;
    }

  • void add(int index, E element) 在此列表中的指定位置插入指定的元素。
public void add(int index, E element) {
        checkPositionIndex(index); //检查索引是否处于[0-size]之间

        if (index == size)//添加在链表尾部
            linkLast(element);
        else//添加在链表中间
            linkBefore(element, node(index));
    }
    
    
    linkBefore方法需要给定两个参数,一个插入节点的值,一个指定的node,
    所以我们又调用了Node(index)去找到index对应的node
  • boolean addAll(Collection<? extends E> c) 按照指定集合的迭代器返回的顺序将指定集合中的所有元素追加到此列表的末尾。

  • boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到此列表中,从指定的位置开始。

public boolean addAll(int index, Collection<? extends E> c) {
        //1:检查index范围是否在size之内
        checkPositionIndex(index);

        //2:toArray()方法把集合的数据存到对象数组中
        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        //3:得到插入位置的前驱节点和后继节点
        Node<E> pred, succ;
        //如果插入位置为尾部,前驱节点为last,后继节点为null
        if (index == size) {
            succ = null;
            pred = last;
        }
        //否则,调用node()方法得到后继节点,再得到前驱节点
        else {
            succ = node(index);
            pred = succ.prev;
        }

        // 4:遍历数据将数据插入
        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;
        }

        //如果插入位置在尾部,重置last节点
        if (succ == null) {
            last = pred;
        }
        //否则,将插入的链表与先前链表连接起来
        else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }    

  • void addFirst(E e) 在该列表开头插入指定的元素。
 public void addFirst(E e) {
        linkFirst(e);
    }
  • void addLast(E e) 将指定的元素追加到此列表的末尾。
public void addLast(E e) {
        linkLast(e);
    }
  • boolean offer(E e) 将指定的元素添加为此列表的尾部(最后一个元素)。

  • boolean offerFirst(E e) 在此列表的前面插入指定的元素。

public boolean offerFirst(E e) { 
       addFirst(e); 
       return true; 
    }
  • boolean offerLast(E e) 在该列表的末尾插入指定的元素。
public boolean offerLast(E e) { 
       addLast(e); 
       return true; 
   }

删除

  • E remove() 检索并删除此列表的头(第一个元素)。
public E remove() {
        return removeFirst();
    }
  • E remove(int index) 删除该列表中指定位置的元素。
public E remove(int index) {
        //检查index范围
        checkElementIndex(index);
        //将节点删除
        return unlink(node(index));
    }
  • boolean remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)。
public boolean remove(Object o) {
        //如果删除对象为null
        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;
    }


当删除指定对象时,只需调用remove(Object o)即可,
不过该方法一次只会删除一个匹配的对象,如果删除了匹配对象,返回true,否则false
  • E removeFirst() 从此列表中删除并返回第一个元素。
public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
  • boolean removeFirstOccurrence(Object o) 删除此列表中指定元素的第一个出现(从头到尾遍历列表时)。

  • E removeLast() 从此列表中删除并返回最后一个元素。

public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
  • boolean removeLastOccurrence(Object o) 删除此列表中指定元素的最后一次出现(从头到尾遍历列表时)。

检索

  • E element() 检索但不删除此列表的头(第一个元素)。
public E element() {
        return getFirst();
    }
  • E get(int index) 返回此列表中指定位置的元素。
public E get(int index) {
        //检查index范围是否在size之内
        checkElementIndex(index);
        //调用Node(index)去找到index对应的node然后返回它的值
        return node(index).item;
    }
  • E getFirst() 返回此列表中的第一个元素。
public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
  • E getLast() 返回此列表中的最后一个元素。
 public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
  • int indexOf(Object 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;
    }
  • int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
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;
    }
  • E peek() 检索但不删除此列表的头(第一个元素)。
public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }
  • E peekFirst() 检索但不删除此列表的第一个元素,如果此列表为空,则返回 null。
public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }
  • E peekLast() 检索但不删除此列表的最后一个元素,如果此列表为空,则返回 null。
 public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }
  • E poll() 检索并删除此列表的头(第一个元素)。

  • E pollFirst() 检索并删除此列表的第一个元素,如果此列表为空,则返回 null 。

// poll的时候如果没有元素返回null
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
  • E pollLast() 检索并删除此列表的最后一个元素,如果此列表为空,则返回 null 。
// poll的时候如果没有元素返回null
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}
  • E pop() 从此列表表示的堆栈中弹出一个元素。
public E pop() {
    return removeFirst();
}

  • void push(E e) 将元素推送到由此列表表示的堆栈上。
public void push(E e) {
    addFirst(e);
}

其他方法

  • Iterator<E> descendingIterator() 以相反的顺序返回此deque中的元素的迭代器。

  • boolean contains(Object o) 如果此列表包含指定的元素,则返回 true

  • void clear() 从列表中删除所有元素。

  • ListIterator<E> listIterator(int index) 从列表中的指定位置开始,返回此列表中元素的列表迭代器(按适当的顺序)。

  • E set(int index, E element) 用指定的元素替换此列表中指定位置的元素。

  • int size() ``返回此列表中的元素数

总结

  1. LinkedList是一个以双链表实现的List

  2. LinkedList还是一个双端队列,具有队列、双端队列、栈的特性;

  3. LinkedList在队列首尾添加、删除元素非常高效,时间复杂度为O(1)

  4. LinkedList在中间添加、删除元素比较低效,时间复杂度为O(n)

  5. LinkedList不支持随机访问,所以访问非队列首尾的元素比较低效;

  6. LinkedList在功能上等于ArrayList + ArrayDeque

以上就是对LinkedList的详细描述

1111.gif