Collection 集合源码剖析

2024-05-30 21:24:22 浏览数 (1)

Collection

ArrayList源码剖析

添加元素分析

ArrayList数组源码剖析

三个构造器

代码语言:javascript复制
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: " 
                                           initialCapacity);
    }
}

/**
     * Constructs an empty list with an initial capacity of ten.
     */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
//将另一个数组直接全部赋值给新的数组,然后初始化
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

对于图中的拷贝如下图

add方法

他不仅可以实现添加,也可以实现插入操作

代码语言:javascript复制
public boolean add(E e) {
        ensureCapacityInternal(size   1);  // Increments modCount!!
        elementData[size  ] = e;
        return true;
    }

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size   1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index   1,
                         size - index);
        elementData[index] = element;
        size  ;
    }

以及对应的addAll方法,也可以是实现插入操作

代码语言:javascript复制
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size   numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size  = numNew;
        return numNew != 0;
    }

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size   numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index   numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size  = numNew;
        return numNew != 0;
    }

Set方法

对于set方法,就像源码中提及的那样,如果判断索引位置数组下标没有越界,那么就直接赋值即可

代码语言:javascript复制
public E set(int index, E element) {
    rangeCheck(index);//下标越界检查
    E oldValue = elementData(index);
    elementData[index] = element;//赋值到指定位置,复制的仅仅是引用
    return oldValue;
}

get方法

获取数据索引位置数据,检查是否越界。如果没有,然后返回即可

代码语言:javascript复制
public E get(int index) {
    rangeCheck(index);
    return (E) elementData[index];//注意类型转换
}

remove方法

传入要删除位置的索引,然后将索引位置后面的元素全部向前挪一位即可(赋值拷贝)

代码语言:javascript复制
public E remove(int index) {
    rangeCheck(index);
    modCount  ;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index 1, elementData, index, numMoved);
    elementData[--size] = null; //清除该位置的引用,让GC起作用
    return oldValue;
}

trimToSize()方法

作用 : 将底层数组的容量调整为当前列表保存的实际元素的大小的功能。 ,以减少内存的开销

代码语言:javascript复制
/**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     */
public void trimToSize() {
    modCount  ;
    if (size < elementData.length) {
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }
}

indexOf()—获取元素第一次出现的位置

代码语言:javascript复制
/**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {
        //因为List可以存储null对象,所以源码地方在这里将其考虑进去,分开进行索引
        if (o == null) {
            for (int i = 0; i < size; i  )
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i  )
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

lastIndexOf() — 获取元素最后一次出现的位置

从最后开始索引,然后出现的第一个就是最后一个。源码牛

代码语言:javascript复制
/**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

LinkedList源码剖析

LinkedList<E> res = new LinkedList<>();

LinkedList同时实现了List接口和Deque接口,所以说我们既可以将其视为一个容器,又可以将其视为一个队列乃至栈

LinkedList底层通过双向链表实现

LinkedList通过firstlast引用分别指向链表的第一个和最后一个元素。注意这里没有所谓的哑元,当链表为空的时候firstlast都指向null

LinkedList它的内部定义了一个私有的Node链表

代码语言:javascript复制
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;
    }
}

三个构造器

transient是Java语言的关键字,用来表示一个成员变量不是该对象序列化的一部分

代码语言:javascript复制
public LinkedList() {
}

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param  c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

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

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

getFirst() 获取第一个元素

只要对LinkedList一设置值,那么last 和first就会被赋值

代码语言:javascript复制
/**
    * Returns the first element in this list.
    *
    * @return the first element in this list
    * @throws NoSuchElementException if this list is empty
    */
   public E getFirst() {
       final Node<E> f = first;
       if (f == null)
           throw new NoSuchElementException();
       return f.item;
   }

getLast() 获取最后一个元素:

代码语言:javascript复制
/**
   * Returns the last element in this list.
   *
   * @return the last element in this list
   * @throws NoSuchElementException if this list is empty
   */
  public E getLast() {
      final Node<E> l = last;
      if (l == null)
          throw new NoSuchElementException();
      return l.item;
  }

删除元素(如下)

removeFirst(), removeLast(), remove(e), remove(index)

从源码中我们可以得出,如果需要删除某个节点,那么就将这个节点传入,或者传入该节点的索引

代码语言:javascript复制
 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;
   }
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; // GC
       size--;
       modCount  ;
       return element;
   }

对于removeFirst 和 removeLast

代码语言:javascript复制
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;
   }
代码语言:javascript复制
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;
   }

add方法

对于Add方法,官网给出了两类添加的方法,一种是直接添加到链表的末尾。还有一种是插入链表。

代码语言:javascript复制
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  ;
   }
代码语言:javascript复制
public void add(int index, E element) {
    //先检查索引位置是否越界
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
	
  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  ;
    }

addAll()

同样,addAll也是有两种构造器,第一个是将另一个集合全部添加到链表的末尾,另一个是将另一个集合添加到指定的位置

代码语言:javascript复制
public boolean addAll(Collection<? extends E> c) {
       return addAll(size, c);
   }

   /**
    * Inserts all of the elements in the specified collection into this
    * list, starting at the specified position.  Shifts the element
    * currently at that position (if any) and any subsequent elements to
    * the right (increases their indices).  The new elements will appear
    * in the list in the order that they are returned by the
    * specified collection's iterator.
    *
    * @param index index at which to insert the first element
    *              from the specified collection
    * @param c collection containing elements to be added to this list
    * @return {@code true} if this list changed as a result of the call
    * @throws IndexOutOfBoundsException {@inheritDoc}
    * @throws NullPointerException if the specified collection is null
    */
   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;
   }

Set方法

对于set方法,就像源码中提及的那样,如果判断索引位置数组下标没有越界,那么就直接赋值即可

代码语言:javascript复制
/**
  * Replaces the element at the specified position in this list with the
  * specified element.
  *
  * @param index index of the element to replace
  * @param element element to be stored at the specified position
  * @return the element previously at the specified position
  * @throws IndexOutOfBoundsException {@inheritDoc}
  */
 public E set(int index, E element) {
     checkElementIndex(index);
     Node<E> x = node(index);
     E oldVal = x.item;
     x.item = element;
     return oldVal;
 }

get方法

获取数据索引位置数据,检查是否越界。如果没有,然后返回即可

代码语言:javascript复制
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

 

Clear()

代码语言:javascript复制
/**
 * Removes all of the elements from this list.
 * The list will be empty after this call returns.
 */
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  ;
}

查看元素(indexOf / lastIndexOf)

和ArrayList集合一样,indexOf作用就是为了返回元素首次出现的位置

而lastIndexOf就是返回元素最后出现的位置

代码语言:javascript复制
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;
}

/**
 * Returns the index of the last occurrence of the specified element
 * in this list, or -1 if this list does not contain the element.
 * More formally, returns the highest index {@code i} such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
 * or -1 if there is no such index.
 *
 * @param o element to search for
 * @return the index of the last occurrence of the specified element in
 *         this list, or -1 if this list does not contain the element
 */
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<Integer> queue = new LinkedList<>();

使用接口 对象名 = new 类名; 方式实例化的对象只能调用接口中有的方法,而不能调用类中特有的方法。

而使用类名 对象名 = new 类名;方式创建出来的对象可以调用所有的方法

对于LinkedList() 它同样可以实现队列

方法有

  • peek() —– 返回队列的头部
  • element() —–返回队列的头部【 此方法与 peek 的不同之处仅在于,如果此队列为空,它会抛出异常。】
  • poll() ——出列
  • offer() —– 入列【使用容量受限队列时,此方法通常更可取, add后者只能通过引发异常来插入元素。】
  • add() ——入列
  • remove() —– 删除队列的头部
代码语言:javascript复制
public E peek() {	
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

/**
 * Retrieves, but does not remove, the head (first element) of this list.
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 检索但不删除此队列的头部。 此方法与 peek 的不同之处仅在于,如果此队列为空,它会抛出异常。
 */
public E element() {
    return getFirst();
}

/**
 * Retrieves and removes the head (first element) of this list.
 *
 * @return the head of this list, or {@code null} if this list is empty
 * @since 1.5
 */
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

/**
 * 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
 检索并删除此队列的头部,如果此队列为空,则返回 null 。
返回值:
此队列的头部,或者 null 如果此队列为空
 */
public E remove() {
    return removeFirst();
}

/**
 * Adds the specified element as the tail (last element) of this list.
 *
 * @param e the element to add
 * @return {@code true} (as specified by {@link Queue#offer})
 * @since 1.5
 */
public boolean offer(E e) {
    return add(e);
}

Deque双端队列

它在queue的基础上又增加了特殊情况,也就是返回值

此接口定义访问双端元素的方法。提供了插入、删除和检查元素的方法。这些方法中的每一个都以两种形式存在:一种在操作失败时引发异常另一种返回特殊值( null 或 false,具体取决于操作)。后一种形式的插入操作专门设计用于容量受限 Deque 的实现;在大多数实现中,插入操作不会失败。

详见源码

代码语言:javascript复制
/**
    * Inserts the specified element at the front of this list.
    *
    * @param e the element to insert
    * @return {@code true} (as specified by {@link Deque#offerFirst})
    * @since 1.6
    */
   public boolean offerFirst(E e) {
       addFirst(e);
       return true;
   }

   /**
    * Inserts the specified element at the end of this list.
    *
    * @param e the element to insert
    * @return {@code true} (as specified by {@link Deque#offerLast})
    * @since 1.6
    */
   public boolean offerLast(E e) {
       addLast(e);
       return true;
   }

   /**
    * Retrieves, but does not remove, the first element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the first element of this list, or {@code null}
    *         if this list is empty
    * @since 1.6
    */
   public E peekFirst() {
       final Node<E> f = first;
       return (f == null) ? null : f.item;
    }

   /**
    * Retrieves, but does not remove, the last element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the last element of this list, or {@code null}
    *         if this list is empty
    * @since 1.6
    */
   public E peekLast() {
       final Node<E> l = last;
       return (l == null) ? null : l.item;
   }

   /**
    * Retrieves and removes the first element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the first element of this list, or {@code null} if
    *     this list is empty
    * @since 1.6
    */
   public E pollFirst() {
       final Node<E> f = first;
       return (f == null) ? null : unlinkFirst(f);
   }

   /**
    * Retrieves and removes the last element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the last element of this list, or {@code null} if
    *     this list is empty
    * @since 1.6
    */
   public E pollLast() {
       final Node<E> l = last;
       return (l == null) ? null : unlinkLast(l);
   }

   /**
    * Pushes an element onto the stack represented by this list.  In other
    * words, inserts the element at the front of this list.
    *
    * <p>This method is equivalent to {@link #addFirst}.
    *
    * @param e the element to push
    * @since 1.6
    */
   public void push(E e) {
       addFirst(e);
   }

   /**
    * Pops an element from the stack represented by this list.  In other
    * words, removes and returns the first element of this list.
    *
    * <p>This method is equivalent to {@link #removeFirst()}.
    *
    * @return the element at the front of this list (which is the top
    *         of the stack represented by this list)
    * @throws NoSuchElementException if this list is empty
    * @since 1.6
    */
   public E pop() {
       return removeFirst();
   }

   /**
    * Removes the first occurrence of the specified element in this
    * list (when traversing the list from head to tail).  If the list
    * does not contain the element, it is unchanged.
    *
    * @param o element to be removed from this list, if present
    * @return {@code true} if the list contained the specified element
    * @since 1.6
    */
   public boolean removeFirstOccurrence(Object o) {
       return remove(o);
   }

   /**
    * Removes the last occurrence of the specified element in this
    * list (when traversing the list from head to tail).  If the list
    * does not contain the element, it is unchanged.
    *
    * @param o element to be removed from this list, if present
    * @return {@code true} if the list contained the specified element
    * @since 1.6
    */
   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;

参考来源: https://pdai.tech/md/java/collection/java-collection-Queue&Stack.html

0 人点赞