java容器類1:Collection,List,ArrayList,LinkedList深入解讀


1、 Iterable 與 Iterator

Iterable 是個接口,實現此接口使集合對象可以通過迭代器遍歷自身元素.

public interface Iterable<T>

修飾符和返回值 方法名 描述
Iterator<T> iterator() 返回一個內部元素為T類型的迭代器
default void forEach(Consumer<? super T> action) 對內部元素進行遍歷,並對元素進行指定的操作
default Spliterator<T> spliterator() 創建並返回一個可分割迭代器

第一個接口iterator()是jdk1.5引入的,需要子類實現一個內部迭代器Iterator遍歷元素。

后兩個接口是JDK1.8后新添加的,forEach(Consumer action)是為了方便遍歷操作集合內的元素,spliterator()則提供了一個可以並行遍歷元素的迭代器,以適應現在cpu多核時代並行遍歷的需求.

其中我們可以看default修飾符,這也是Java 8后新出現的,我們知道,如果我們給一個接口新添加一個方法,那么所有他的具體子類都必須實現此方法,為了能給接口拓展新功能,而又不必每個子類都要實現此方法,Java 8新加了default關鍵字,被其修飾的方法可以不必由子類實現,並且由dafault修飾的方法在接口中有方法體,這打破了Java之前對接口方法的規范.

Iterator 為迭代器類,用於遍歷容器。

public interface Iterator<E>

修飾符和返回值 方法名 描述
boolean hasNext() 判斷迭代器所指元素是否為最后一個
E next() 下一個元素
default Void remove() 移除迭代器只想的當前元素
default Void forEachRemaining(Consumer<? super E> action) 遍歷迭代器指向元素后面的所有元素

AbstartList中實現了Iterator類作為List內部的迭代器,用於訪問內部元素,其代碼如下:

  1 private class Itr implements Iterator<E> {
  2         /**
 3          * Index of element to be returned by subsequent call to next.
 4          */
  5         int cursor = 0;
  6 
  7         /**
 8          * Index of element returned by most recent call to next or
 9          * previous.  Reset to -1 if this element is deleted by a call
 10          * to remove.
 11          */
 12         int lastRet = -1;
 13 
 14         /**
 15          * The modCount value that the iterator believes that the backing
 16          * List should have.  If this expectation is violated, the iterator
 17          * has detected concurrent modification.
 18          */
 19         int expectedModCount = modCount;
 20 
 21         public boolean hasNext() {
 22             return cursor != size();
 23         }
 24 
 25         public E next() {
 26             checkForComodification();
 27             try {
 28                 int i = cursor;
 29                 E next = get(i);
 30                 lastRet = i;
 31                 cursor = i + 1;
 32                 return next;
 33             } catch (IndexOutOfBoundsException e) {
 34                 checkForComodification();
 35                 throw new NoSuchElementException();
 36             }
 37         }
 38 
 39         public void remove() {
 40             if (lastRet < 0)
 41                 throw new IllegalStateException();
 42             checkForComodification();
 43 
 44             try {
 45                 AbstractList.this.remove(lastRet);
 46                 if (lastRet < cursor)
 47                     cursor--;
 48                 lastRet = -1;
 49                 expectedModCount = modCount;
 50             } catch (IndexOutOfBoundsException e) {
 51                 throw new ConcurrentModificationException();
 52             }
 53         }
 54 
 55         final void checkForComodification() {
 56             if (modCount != expectedModCount)
 57                 throw new ConcurrentModificationException();
 58         }
 59     }
 60 
View Code

2、Collection

Collection 是容器類的接口,里面可以存放很多Elements,很多容器都實現了該接口。

public interface Collection<E> extends Iterable<E>

修飾符和返回值 方法名 描述
int size() 判斷容器的大小
boolean isEmpty() 判空
boolean contains(Object o) 是否包含某元素
Object[] toArray() 轉化為數組
<T> T[] toArray(T[] a) 將容器中所有元素拷貝到a中,如果a不夠大,則拷貝到返回的數組中。(見AbstactCollection中實現)
boolean add(E e) 添加元素
boolean remove(Object o) 移除容器中第一個出現Object對象,如果Object為null,則移除第一個出現的null。如果沒有Object對象返回false
boolean containsAll(Collection<?> c) 包含c中所有元素
boolean addAll(Collection<? extends E> c); 將c中所有元素添加到容器中
boolean removeAll(Collection<?> c) 如果容器中包含c中的元素,刪除。(調用remove(Object))
default boolean removeIf(Predicate<? super E> filter) 移除所有符合條件的元素(JDK1.8中引入)
boolean retainAll(Collection<?> c) 保留在c中出現的元素,其它全部移除
boolean clear()  
boolean equals(Object o) 與o中所有元素都相等則相等
int  hashCode() c1.equals(c2) 那么他們的hashCode()一定相等,反之不成立 
default Spliterator<E> spliterator() 在所有元素之上創建一個Spliterator
default Stream<E> stream()  
default Stream<E> parallelStream()  

上述很多函數的實現可以參考 AbstactList中的實現。

3. List

List是一個接口,繼承了Collection中的所有接口,並且添加了自身相關接口和具體實現。

由上圖可以看出,Collection分成了三個分支,List就是其中一個,下面我們具體分析一下增加的接口。

public interface List<E> extends Collection<E>

修飾符和返回值 方法名 描述
  Collection中的所有接口  
default void  replaceAll(UnaryOperator<E> operator) 將運算操作后的結果替換List中原有元素
default void sort(Comparator<? super E> c) 將List中所有元素排序
E  get(int index);  
E  set(int index, E element)  
E  remove(int index)  
int indexOf(Object o) o在List中第一次出現的位置
int lastIndexOf(Object o) o在List中最后一次出現的位置
ListIterator<E> listIterator() 獲取List的迭代器
ListIterator<E> listIterator(int index) 獲取從某個位置開始的迭代器,index為迭代器起始位置
List<E> subList(int fromIndex, int toIndex) 子List

上表可以看出,增加了 和index相關的接口,根據index對List進行的get、set、remove等操作。另一類添加的接口是ListIteator相關的,獲取List的迭代器。

3.1 ListIterator

public interface ListIterator<E> extends Iterator<E>

ListIterator 不光包含Iterator中的三個接口還增加了作為一個List迭代器應該有的接口,如 next(),previous()等

修飾符和返回值 方法名 描述
  Iterator中的所有接口  
boolean  hasNext()  
E next()  
boolean  hasPrevious()  
E previous()  
int nextIndex()  
int previousIndex()  
void  remove() 刪除next()返回元素
void  set(E e) 替換next()返回的元素
void  add(E e) 在nextIndex()后插入元素

首先需要明確ListIterator迭代器的作用:

  1. 允許我們向前、向后兩個方向遍歷 List;
  2. 在遍歷時修改 List 的元素;
  3. 遍歷時獲取迭代器當前游標所在位置。
注意,迭代器 沒有當前所在元素一說,它只有一個游標( cursor )的概念,這個游標總是在元素之間,比如這樣:

迭代器的初始位置:

這里寫圖片描述

調用next()后迭代器的位置這里寫圖片描述

調用 previous() 游標就會回到之前位置。當向后遍歷完元素,游標就會在元素 N 的后面

這里寫圖片描述

也就是說長度為 N 的集合會有 N+1 個游標的位置。

3.2 AbstractCollection / AbstractList

        在上面的繼承關系圖中並沒有顯示出AbstractCollect和AbstractList的存在,但是在閱讀源碼的時候經常遇到這兩個類,下面講一下這兩個類的作用。

      首先需要明確的一點是AbstractCollect和AbstractList都是抽象類而不是接口,它們分別實現了Collection和List接口中的部分函數。這樣繼承者直接繼承AbstractXXXX 而不需要自己重復實現里面的所有接口。這兩個抽象類抽離了公共部分,進行了實現,減少后續類的工作量。

       ArrayList 和LinkedList都直接或者間接繼承了AbstartList類。下圖為這兩個類的繼承關系:

這里寫圖片描述

        具體這兩個類里面實現了哪些接口,請自己閱讀源碼不再講解。

LinkedList中modCount和expectedModCount作用

在AbstractList中可以看到這兩個變量,它們用於表示List被修改的次數,這其中包括了調用集合本身的add等方法等修改方法時進行的修改和調用集合迭代器的修改方法進行的修改。而expectedModCount則是表示迭代器對集合進行修改的次數。

那么知道這兩個值有什么作用呢?

如果創建多個迭代器對一個集合對象進行修改的話,那么就會有一個modCount和多個expectedModCount,且modCount的值之間也會不一樣,這就導致了moCount和expectedModCount的值不一致,從而產生異常。

https://www.cnblogs.com/zhangcaiwang/p/7131035.html

3.3 ArrayList

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
我們看到ArrayList的定義的時候很可能會出現一個疑問,為什么它已經繼承了AbstractList了還需要去實現List<E>接口? 答案是,后面的List<E>可以去掉,這里只是讓閱讀者明白ArrayList是個List。 終於好不容易到了開發者常用的類了,有點竊喜:transient Object[] elementData;這個很重要的成員變量elementData數組用來存放ArrayList中的元素,當第一個元素添加進來后,它的默認長度變為10。(java中“transient”關鍵字修飾的成員變量在類序列化的過程中不會被持久化到文件中,保證該成員變量保存信息的安全。)
ArrayList的構造函數中可以直接指定elementData數組的長度,那么問題來了,當數組已經完全被占用再向ArrayList中添加元素時,如何再分配更大長度的數組?如何把舊數據拷貝到新數組中?答案見下面這段源碼

  1 private void grow(int minCapacity) {  //最少需要的容量 
  2         // overflow-conscious code
  3         int oldCapacity = elementData.length;
  4         int newCapacity = oldCapacity + (oldCapacity >> 1);   // 分配最小容量的 1.5倍
  5         if (newCapacity - minCapacity < 0)       
  6             newCapacity = minCapacity;
  7         if (newCapacity - MAX_ARRAY_SIZE > 0)
  8             newCapacity = hugeCapacity(minCapacity);   // 最大容量比較(Inter.MAX_VALUE)
  9         // minCapacity is usually close to size, so this is a win:
 10         elementData = Arrays.copyOf(elementData, newCapacity);  // 將舊數據拷貝到新數組中
 11     }

其它接口不再細說。

3.4 LinkedList

 

  1 public class LinkedList<E>
  2     extends 
AbstractSequentialList<E>
  3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable

作為一個鏈表類型,首先需要熟悉一下節點類:Node(LinkedList靜態內部類)

  1 private static class Node<E> {
  2         E item;
  3         Node<E> next;
  4         Node<E> prev;
  5 
  6         Node(Node<E> prev, E element, Node<E> next) {
  7             this.item = element;
  8             this.next = next;
  9             this.prev = prev;
 10         }
 11     }

在LinkedList中有三個主要成員變量:

  • transient int size = 0;      // 鏈表長度
  • transient Node<E> first;  // 鏈表的首節點
  • transient Node<E> last;  // 鏈表的末節點

LinkedList構造方法:

  1  /**
 2      * Constructs an empty list.
 3      */
  4     public LinkedList() {
  5     }
  6 
  7     /**
 8      * Constructs a list containing the elements of the specified
 9      * collection, in the order they are returned by the collection's
 10      * iterator.
 11      *
 12      * @param  c the collection whose elements are to be placed into this list
 13      * @throws NullPointerException if the specified collection is null
 14      */
 15     public LinkedList(Collection<? extends E> c) {
 16         this();
 17         addAll(c);
 18     }

clear()方法:遍歷LinkedList 置null

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

public E set(int index, E element):將某個Index指向的Node置變為element

public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);    //node(Int i) 遍歷LinkedList查找第i個Node
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }
View Code

public void add(int index, E element):在鏈表的index位置添加一個節點

這里還有眾多的插入刪除等函數,其主要思想還是對鏈表的操作(插入刪除等),詳情閱讀源碼。

3.5 ArrayList 與LinkedList 性能比較

兩種List的存儲結構決定了兩者的性能:ArrayList以數組形式存儲,LinkedList以鏈表形式存儲

ArrayList是以數組的形式存儲的,所以他的遍歷很快,可以根據index很快找到對應元素。但是,ArrayList的插入和刪除操作較慢,如果需要在數組中插入一個元素或者刪除一個元素較麻煩。

LinkedList的索引操作較慢(需要遍歷列表才能找到對應索引元素),但是它的插入和刪除操作效率高(只需要控制前后指針就能實現插入和刪除)。


參考:

https://www.cnblogs.com/bushi/p/6647006.html

https://www.jianshu.com/p/047e33fdefd2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM