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

一、 Iterable 與 Iterator

Iterable 是個接口,實現此接口使集合對象能夠經過迭代器遍歷自身元素.html

public interface Iterable<T>java

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

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

後兩個接口是JDK1.8後新添加的,forEach(Consumer action)是爲了方便遍歷操做集合內的元素,spliterator()則提供了一個能夠並行遍歷元素的迭代器,以適應如今cpu多核時代並行遍歷的需求.web

其中咱們能夠看default修飾符,這也是Java 8後新出現的,咱們知道,若是咱們給一個接口新添加一個方法,那麼全部他的具體子類都必須實現此方法,爲了能給接口拓展新功能,而又沒必要每一個子類都要實現此方法,Java 8新加了default關鍵字,被其修飾的方法能夠沒必要由子類實現,而且由dafault修飾的方法在接口中有方法體,這打破了Java以前對接口方法的規範.api

Iterator 爲迭代器類,用於遍歷容器。數組

public interface Iterator<E>安全

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

AbstartList中實現了Iterator類做爲List內部的迭代器,用於訪問內部元素,其代碼以下:oracle

  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

二、Collection

Collection 是容器類的接口,裏面能夠存放不少Elements,不少容器都實現了該接口。dom

public interface Collection<E> extends Iterable<E>ide

修飾符和返回值 方法名 描述
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的值不一致,從而產生異常。

http://www.javashuo.com/article/p-zomxxblm-ba.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的索引操做較慢(須要遍歷列表才能找到對應索引元素),可是它的插入和刪除操做效率高(只須要控制先後指針就能實現插入和刪除)。


參考:

http://www.javashuo.com/article/p-uftmmdof-z.html

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

相關文章
相關標籤/搜索