集合之LinkedList

青春壹個敷衍的年華 提交于 2019-12-04 08:26:35

在JDK中,实现了链表这种数据结构,就是LinkedList,它是双向链表的实现。首先看看它的继承层次。了解它的功能:

LinkedList继承层次

  • LinkeList继承的抽象类是AbstractSequentialList,在这个抽象类中有几个随机访问元素的方法,这与LinkedList基于链表实现相关,因为链表可以快速的add和remove而无需移动元素。上源码:
    /**
     * 在指定的index插入一个元素,原来位置的元素往后靠
     * 
     */
    public void add(int index, E element) {
        try {
        	// 调用listIterator的add方法,如果没有实现就会报错 UnsupportedOperationException
            listIterator(index).add(element);
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }

    /**
     * 
     */
    public E remove(int index) {
        try {
        	// 根据index的到listIterator
            ListIterator<E> e = listIterator(index);
            // 这里注意,listIterator使用remove之前,需要next调用一下
            E outCast = e.next();
            e.remove();
            return outCast;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
  • AbstractSequentialList 的add和remove方法都是调用ListIterator 声明的,实现是根据集合的类型来的,如果没有实现的话,就会抛出UnsupportedOperationException。
  • 但是在LinkeList 中,针对这些方法都重新实现了:
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

如果插入的index和集合的大小相等,那么直接加在后面

    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++;
    }

如果不是最后一个那么就需要加在中间的位置:

    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++;
    }

这里还需要node方法返回index所在的节点:

    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;
        }
    }

remove方法和add方法实现是差不多的,我们可以看出LinkedList的add和remove方法都针对链表的特性重写了。

从继承层次中我们可以看到LinkedList还实现了Deque接口,而这个接口来自于Queue,在数据结构中,队列是一种先进先出的数据结构,看看在JDK中,定义了哪些方法:

public interface Queue<E> extends Collection<E> {
    
    // 添加一个元素到队列尾,容量不够时抛出异常
    boolean add(E e);

    // 添加一个元素到队列尾,容量不够返回false
    boolean offer(E e);

    // 返回并删除队列头的元素,为空时返回null
    E remove();

    //  返回并删除队列头的元素。 如果为为空抛出异常
    E poll();

    // 返回但不删除队列的头元素,为空时抛出异常
    E element();

    // 返回但不删除队列的头元素,为空时返回null
    E peek();
}
  • 添加,删除,返回对头都有两个方法,注意区别就是是否在空时抛出异常还是返回null

Deque继承了Queue接口,并在次基础上添加了在队列两端都可以访问的方法,是一个双向队列。然后在LinkedList中使用链表做了实现。

还有一点就是,我们知道链表的特点就是可以快速的随机add和remove,但是对于随机的get和set,就需要使用迭代器遍历链表知道找到,在JDK中,LinkedList对于set和get方法依然实现,并做了折半的优化:

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
	
    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;
        }
    }
  • 在node方法中,如果index小于size /2 ,那么从前往后找,否则就从后往前找。

在继承层次中,LinkedList还是实现了Cloneable接口,表示一个链表是可以克隆的,看看重写的clone方法:

    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;
    }
	
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
  • 根据Object类的clone方法创建了一个LinkedList,然后再依次把每个元素复制到新集合中并返回,并且是浅拷贝的。

其他的就都是一些集合通用的方法了,在Collection接口或者在AbstractCollection中实现的。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!