Java 容器相關知識全面總結

Java實用類庫提供了一套至關完整的容器來幫助咱們解決不少具體問題。由於我自己是一名Android開發者,包括我在內不少安卓開發,最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList三劍客, 平時接觸使用的容器也只有ArrayList和HashMap。致使對於整個Java容器體系的掌握和使用還停留在很淺的層面。省不足而思改進,那麼跟着我來總結一下Java容器的相關知識吧。html

結構

  • java容器類的繼承結構java

  • 具體介紹算法

    • List編程

    • Set設計模式

    • Queue數組

    • 迭代器安全

    • Collection多線程

    • Map併發

  • 一些建議ide

  • 進階·併發容器

    • CopyOnWriteArrayList與Copy-On-Write策略

    • ConcurrentLinkedQueue

    • ConcurrentHashMap與鎖分段技術

    • 阻塞隊列

java容器類的繼承結構

Java容器類庫定義了兩個不一樣概念的容器,Collection和Map

  • Collection 一個獨立元素的序列,這些元素都服從一條或多條規則。List必須按照插入的順序保存元素。Set不能有重複元素。Queue按照排隊規則來肯定對象產生的順序。

(文中Jdk源碼版本無特殊說明均爲jdk1.8.0_101)

    public interface Collection<E> extends Iterable<E> {        int size();        boolean isEmpty();        boolean contains(Object o);        Iterator<E> iterator();

        Object[] toArray();

        <T> T[] toArray(T[] a);        boolean add(E e);        boolean remove(Object o);        boolean containsAll(java.util.Collection<?> c);        boolean addAll(java.util.Collection<? extends E> c);        boolean removeAll(java.util.Collection<?> c);

        ... //省略了其餘方法
    }

能夠看到,java定義了Collection接口和內部集合的基本操做方法,Collection默承認以進行對集合末端添加元素,刪除指定元素等操做。List、Set、Queue接口都繼承自Collection並定義了各自不一樣的方法。

  • Map 一組成對的」鍵值對」對象,容許咱們使用鍵來查找值。

    public interface Map<K,V> {        int size();        boolean containsKey(Object key);        boolean containsValue(Object value);        V get(Object key);        V put(K key, V value);        V remove(Object key);        void putAll(java.util.Map<? extends K, ? extends V> m);        void clear();        Set<K> keySet();        Collection<V> values();

        Set<java.util.Map.Entry<K, V>> entrySet();        interface Entry<K,V> {            K getKey();            V getValue();            V setValue(V value);            boolean equals(Object o);            int hashCode();

            ...
        }        boolean equals(Object o);        int hashCode();

    }

Map內部接口Entry<K,V>對應着Map的鍵值對。

具體介紹

迭代器

先介紹一下迭代器。迭代器自己也是一種設計模式,設計的初衷在於:容器的實現由不少種,而咱們想對容器進行遍歷操做的話,首先不該該關心容器實現的細節,其次遍歷操做應該是輕量級的。迭代器統一了對容器的訪問方式,同時建立它的代價很小。值得注意的是,Iterator只能單向移動。

    public interface Iterator<E> {        boolean hasNext();        E next();        default void remove() {            throw new UnsupportedOperationException("remove");
        }        default void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);            while (hasNext())
                action.accept(next());
            }
    }

經過容器的iterator()方法拿到容器的迭代器
迭代器的next()獲取下一個元素
hasNext()判斷是否還有元素
remove()刪除指定元素

ListIterator

ListIterator是Iterator的擴展以內,用於各類List類訪問,支持雙向移動。

Collection

List

List 承諾能夠將元素維護在特定的序列中.List接口在Collection的基礎上添加了大量的方法,使得能夠再List中間插入和移除元素。

    public interface List<E> extends Collection<E> {

        ...        boolean add(E e);        boolean remove(Object o);        boolean containsAll(Collection<?> c);        boolean addAll(Collection<? extends E> c);        boolean addAll(int index, Collection<? extends E> c);        boolean removeAll(Collection<?> c);        boolean retainAll(Collection<?> c);        E get(int index);        E set(int index, E element);        void add(int index, E element);        E remove(int index);        int indexOf(Object o);        int lastIndexOf(Object o);

        java.util.List<E> subList(int fromIndex, int toIndex);

        ...

    }

有兩種類型的List,ArrayList和LinkedList

List類型 優勢 缺點 底層實現
ArrayList 隨機訪問元素較快 中間元素的插入和刪除較慢 數組
LinkedList 中間元素的插入和刪除,順序訪問的優化 隨機訪問元素較慢 雙向鏈表

Set

Set不保存重複的元素,一般用於快速查找元素。值得一提的是,Set具備與Collection徹底同樣的接口,沒有任何額外的功能。 存入的元素必須定義equals()方法

Set類型 使用場景 底層實現
HashSet 快速查找,元素必須定義hashCode() 鏈表
TreeSet 保持次序,元素必須實現Comparable接口 紅-黑樹結構
LinkedHashSet 維護次序的HashSet, 元素必須定義hashCode() 鏈表

Queue

除了併發應用,Queue僅有的兩個實現是LinkedList和PriorityQueue, 其中LinkedList同時實現了List, Deque接口。它們的差別在於排序行爲而不是性能。

    public interface Queue<E> extends Collection<E> {        boolean add(E e);        boolean offer(E e);        E remove();        E poll();        E element();        E peek();
    }

Map

Map類型 使用場景 底層實現
HashMap 快速查詢 散列表
LinkedHashMap 迭代遍歷具備順序(插入順序 or 最近最少使用) 鏈表
TreeMap 具備排序,惟一能夠返回子樹的Map(subMap()) 紅-黑樹結構
WeakHashMap 弱鍵映射,映射以外無引用的鍵,能夠被垃圾回收 散列表
ConcurrentHashMap 線程安全的Map 鏈表
IdentityHashMap 使用==代替equals()對鍵進行排序,專位解決特殊問題 鏈表

咱們能夠手工調整HashMap來調整性能,涉及到如容量、初始容量、尺寸、負載因子等概念。感興趣的話能夠看一些相關資料。

一些建議

  • 不要使用過期的容器 如Vector Enumeration Hashtable Stack(沒錯,這就是java最初的糟糕設計,實際中使用棧的話推薦LinkedList)

進階·併發容器

這裏不會討論的太細緻的實現,僅僅簡單介紹一下基礎知識,感興趣的能夠閱讀《Java 併發編程的藝術》這本書。

CopyOnWriteArrayList與Copy-On-Write策略

Copy-On-Write簡稱COW,是一種用於程序設計中的優化策略。其基本思路是,從一開始你們都在共享同一個內容,當某我的想要修改這個內容的時候,纔會真正把內容Copy出去造成一個新的內容而後再改,這是一種延時懶惰策略。從JDK1.5開始Java併發包裏提供了兩個使用CopyOnWrite機制實現的併發容器,它們是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器很是有用,能夠在很是多的併發場景中使用到。

CopyOnWrite容器即寫時複製的容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對CopyOnWrite容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。

CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。因此若是你但願寫入的的數據,立刻能讀到,請不要使用CopyOnWrite容器。

ConcurrentLinkedQueue

在併發編程中,有時候須要使用線程安全的隊列或列表。一般實現線程安全有兩種方式,一種是使用阻塞算法,一種是使用非阻塞算法。非阻塞算法實現基礎爲循環CAS(Compare and Swipe 比較和交換)。

ConcurrentLinkedQueue技術上的實現與CopyOnWriteArrayList與Copy相似,可是容器只有部份內容而不是整個容器能夠被複制和修改。ConcurrentLinkedQueue有head節點和tail節點組成,每一個節點由節點元素(item)和指向下一個結點(next)的引用組成。節點之間經過next關聯起來,造成一張鏈表結構的隊列。

ConcurrentHashMap與鎖分段技術

ConcurrentHashMap是線程安全且高效的HashMap。多線程環境下,使用非線程安全的HashMap會致使死循環,而如文章中建議的那樣,HashTable這種過期容器效率低下(使用synchronized來保證線程安全)。ConcurrentHashMap使用鎖分段技術,大大提升了併發使用的效率。

鎖分段技術: 假設容器有多把鎖,每一把鎖用於鎖容器其中一部分數據,當多線程訪問容器不一樣數據段數據時,線程間就不存在鎖競爭,從而提升併發訪問效率。

阻塞隊列

JDK7 提供了7個阻塞隊列,實現原理都是基於生產-消費模式的等待通知機制。

阻塞隊列類型 特色
ArrayBlockingQueue 由數組結構組成的有界阻塞隊列
LinkedBlockingQueue 由鏈表結構組成的有界阻塞隊列
PriorityBlockingQueue 支持優先級排序的×××阻塞隊列
DelayQueue 使用優先級隊列實現的×××阻塞隊列
SynchronousQueue 不儲存元素的阻塞隊列
LinkedTransferQueue 由鏈表結構組成的×××阻塞隊列
LinkedBlockingQueue 由鏈表結構組成的雙向阻塞隊列

感謝閱讀~

相關文章
相關標籤/搜索