集合之LinkedList面试不再困难

集合

LinkedList(有序的,线程不安全)

  • 为什么是有序

    数据结构是双向链表,但是first和last分别的头结点和未节点不会存放数据-及(first.pre==null;last.next==null)

  • 为什么线程不安全

    因为他的add和remove方法没有加锁,当多线程访问的时候会出现线程安全问题,可以通过 Collections.synchronizedList()解决,或者自己使用Vector,或者自己封装一下都行

  • 内存结构

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

    双向链表


    linkedList_Pic1.png
  • 添加数据

add(E e)

源码:

 public boolean add(E e) {
       linkLast(e);
       return true;
   }


   /**
    * Links e as last element.
    */
   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++;
   }

可以看到是直接添加到最后的。原理:创建一个节点(pre为改链表的last,next为null,element=e),然后将last改为创建的节点newNode,last.next=newNode

add(int index, E element)

源码:

public void add(int index, E element) {
       checkPositionIndex(index);

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

   /**
    * Returns the (non-null) Node at the specified element 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;
       }
   }


   /**
    * Inserts element e before non-null Node 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);
       succ.prev = newNode;
       if (pred == null)
           first = newNode;
       else
           pred.next = newNode;
       size++;
       modCount++;
   }

1,首先判断是不是添加到最后一个节点,如果添加到最后一各节点的话那么直接执行add(e)对应的底层调用linkLast(e)

2,不是最后一个节点,那么先获取这个index所在的节点(node(index))

(1),直接折办(size>>1),在前面的顺序循环查找找到index,node.next,在后面倒叙查找index.pred

3,找到了这个index对应的节点,先获取他的pred(因为我们要插入到这个index对应的位置,让他本省往后移动),然后创建新的节点,Node(pred=获取出来indexNode的pred,next就是indexNode,element就是我们传进来的element),然后将pred的next节点指向创建的新节点,这样就完成了添加到某个位置

remove(index)

源码:

 /**
     * Retrieves and removes the head (first element) of this list.
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    public E remove() {
        return removeFirst();
    }
    /**
     * Removes and returns the first element from this list.
     *
     * @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);
    }

    /**
     * Unlinks non-null first node f.
     */
    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;
    }

1,直接找到first,然后取出他的next(作为替代新的first),

2,先断老first的链(first.next=null)

3,清楚元素(first.item=null),

4,然后将first的nextNode变成心得first(first=nextNode),

5,nextNode的pred是以前的first,因为LinkedList的first头部不存数据的,所以让nextNode.pred=null

removeLast

源码:

    /**
     * Removes and returns the last element from this list.
     *
     * @return the last element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
  /**
     * Unlinks non-null last node l.
     */
    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;
    }

原理和remove()方法差不多看看就知道了,脑海中一定要有内存模型的概念

remove(int index)

源码:


 /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @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));
    }

 /**
     * Returns the (non-null) Node at the specified element 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;
        }
    }


    /**
     * Unlinks non-null node x.
     */
    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;
    }

1 还是先去找index对应的节点

2,取出indexNode的predNode和nextNode

3,predNode的next指向nextNode,nextNode的pred指向predNode

LinkedList你掌握了吗?,脑海中有LinkedList的数据结构的图其实原理都不是很难了。

本人能力有限,只能分析到这儿了,谢谢您的支持!接下来的文章我们会分析常用的集合以及一些Android框架原理和流程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。