這一節我們來總結一下Java集合類。
Java集合總結
繼承與Collection接口的–List接口
List接口本身的特點
常見的繼承List接口的實用類
實用類對比
繼承與Collection接口的–Set接口
Set接口本身的特點
常見的繼承Set接口的實用類
實用類對比
繼承與Collection接口的–Queue接口
Queue接口本身的特點
常見的繼承Queue接口的實用類
特例獨立的–Map接口
Map接口本身的特點
常見的繼承Map接口的實用類
實用類對比
操作集合的工具類:Collections
Java集合總結
Java集合是每個Java程序員在日常開發中都會使用到,而且有時候使用得好的話,能事半功倍。細數Java集合,其實比較常見的就是List、Set、Map和Queue,在這四者之中,除了Map之外,其他三個接口都繼承於Collection。
在這里,首先我們要明確的是,List、Set、Map和Queue其實只是以接口的形式存在着的,所以在日常的程序開發中,請不要出現說想要直接初始化它們的想法和做法,雖然筆者也曾經犯過這樣子的錯誤。
繼承與Collection接口的–List接口
List接口本身的特點
List接口在Java的集合類中充當的是一個元素有序、元素可重復的集合角色。List繼承於Collection集合,故其擁有了Collection集合的全部方法,同時,List集合也擁有屬於自己的方法:用來實現根據元素索引來操作集合元素的作用。通過List集合的源碼(JDK1.7)我們簡單地看看List集合包含的方法:
package java.util; public interface List extends Collection { int size(); //集合元素的數量 boolean contains(Object o); //是否包含某變量,包含返回ture Iterator<E> iterator(); Object[] toArray(); //將集合轉化為一個數組,所有的集合元素變成相應的數組元素 <T> T[] toArray(T[] a); //將集合轉化為一個類型T數組 boolean add(E e); //增加元素,成功返回true boolean remove(Object o); //移除元素,成功返回true boolean containsAll(Collection<?> c); //是否包含集合c中的全部元素,若是返回true boolean addAll(Collection<? extends E> c); //添加整個集合c中全部元素, boolean addAll(int index, Collection<? extends E> c); boolean removeAll(Collection<?> c); //移除該List集合中包含的c中的全部元素 boolean retainAll(Collection<?> c); //是否包含c集合中的元素,包含返回ture void clear(); //清空集合 boolean equals(Object o); int hashCode(); E get(int index); //根據index取元素值 E set(int index, E element); //根據index把List該元素重新復制element void add(int index, E element); //根據index添加新元素element E remove(int index); int indexOf(Object o); //返回對象o在List集合中第一次出現的位置索引 int lastIndexOf(Object o); //返回對象o在List集合中最后一次出現的位置索引 ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); List<E> subList(int fromIndex, int toIndex); //截斷集合 }
常見的繼承List接口的實用類
- ArrayList
ArrayList是基於數組實現的List類,故其封裝了一個動態的、允許再分配的Object[]數組,ArrayList用initialCapacity參數來設置該數組的長度,當長度超過預設值后,ArrayList會動態增加。
ArrayList類是線程不安全的,如果要保證該集合的同步性,必須在程序中手動保證。
值得注意的是:雖說與以鏈表形式建立的LinkedList相比,ArrayList()的插入和修改速度較慢,但那個也是建立在數據量較大的情況下的,在數據量較小的情況下,ArrayList()不一定比LinkedList()方法要慢。另外,ArrayList在末尾插入和刪除數據的話,速度反而比LinkedList要快 - LinkedList
LinkedList(): 在實現中采用鏈表數據結構。插入和刪除速度快,訪問速度慢。
因為除了繼承List接口外,LinkedList接口也繼承了Deque接口,故也可以當作“棧”和隊列(雙向隊列)來使用。因此LinkList接口也會多出一些Deque接口方面的方法,如offer()(將元素接到隊列的尾部)、push()或offerFirst()(將元素加入棧的頂部)、peek()或peekFirst()(訪問但是不刪除棧頂元素)、pop()或者pollLast()(將棧頂元素彈出棧)等方法。 - Vector
Vector與ArrayList十分地相像,都是基於數組實現的List類,其也是封裝了一個動態分配的Object[]數組,也可以使用initialCapacity參數來設置該數組的長度。
Vector是線程安全的,但是也因此Vector的性能很差。 - Stack
Stack是繼承與Vector的子類,它主要用來模擬”棧“,因此也具備了peek()、pop()、push()等主要用於棧操作的方法。
由於Stack是一個比較古老的Java集合類,它同樣是線程安全的,但也因此暴露出了性能較差的缺點,如果程序中要使用棧這樣的數據結構的話,可以試一試ArrayDeque,該類也是List的實現類,但和LinkedList一樣也繼承了Deque接口。
實用類對比
分類 | ArrayList | LinkedList | Vector | Stack |
---|---|---|---|---|
實現形式 | 動態數組 | 鏈表 | 動態數組 | 動態數組 |
線程安全性 | 線程不安全 | 線程不安全 | 線程安全 | 線程安全 |
優點 | 性能較高,隨機訪問速度快 | 插入和刪除速度快 | 線程安全 | 線程安全 |
缺點 | 插入與刪除元素的速度慢 | 隨機訪問速度訪問速度慢 | 性能較差 | 性能較差 |
使用List集合有以下建議:
1、遍歷集合元素。ArrayList和Vector使用get()方法來獲取遍歷元素,LinkedList應該采用迭代器來遍歷集合元素。
2、插入和刪除。當這類操作較多的時候,優先考慮使用LinkedList。
3、多線程。當需要使用到多線程的ArrayList時,可以使用Collections將該集合類包裝成線程安全的集合。
繼承與Collection接口的–Set接口
Set接口本身的特點
Set代表的是無序的、不可重復的集合。因此在Set集合中加入數據元素時,Set集合通常不用記住元素的添加順序。不可重復則是說當將兩個相同的元素加入到一個Set集合中,則添加操作失敗,add()方法返回false,且新元素不會被添加。
public interface Set extends Collection { int size(); //集合元素的數量 boolean isEmpty(); //判斷集合是否為空 boolean contains(Object o); //是否包含某變量,包含返回ture Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); boolean containsAll(Collection<?> c); //是否包含c集合中的元素,包含返回ture boolean addAll(Collection<? extends E> c); //添加包含c集合中所有的元素,包含返回ture boolean retainAll(Collection<?> c); boolean removeAll(Collection<?> c); void clear(); boolean equals(Object o); int hashCode(); }
常見的繼承Set接口的實用類
- HashSet
HashSet按照Hash算法來存儲集合中的元素,因此具有良好的存取和查找功能。
HashSet有三個特點:與TreeSet不同,HashSet是無序的;不是線程同步;集合元素可以是null值。
HashSet集合的存儲過程:當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然后根據該值決定該對象在HashSet中的存儲位置。如果有兩個元素通過equals()方法返回true,但他們的hashCode()方法返回值不相等,HashSet也會將其存儲在不同的位置。也就是說:HashSet的添加元素判斷標准是:兩個對象通過equals()方法比較相等,並且兩個對象的hashCode()方法返回值也相等。 - LinkedHashSet
LinkedHashSet是繼承於HashSet的子類。
但是與父類不同的是,LinkedHashSet用以鏈表的形式來維護元素的次序,也就是說:LinkedHashSet是有序的。遍歷該集合時輸出順序為添加順序。 - TreeSet
TreeSet是SortedSet接口的實現類,所以TreeSet可以確保集合元素處於排序狀態。TreeSet使用紅黑樹來維護集合元素的次序。如果實現comparator()方法,可以實現定制排序。如果采用自然排序,則返回null。
TreeSet集合的特有方法:first()、last()、lower(Object e)(返回指定元素之前的元素)、higher(Object e)(返回指定元素之后的元素)等等。
下面我們說一說TreeSet的定制排序和自然排序:
1、 定制排序
通過Comparator接口的幫助,實現降序排序。
2、 自然排序
TreeSet通過CompareTo(Object obj)比較元素之間的大小關系,將集合元素按升序排列。實現元素必須實現Comparable元素,而且在比較的時候如果出現不同類型時要轉換類型后比較。 - EnumSet
EnumSet是專門為枚舉類設計的集合類,EnumSet中的所有元素都必須是指定枚舉類型的枚舉值,該枚舉類型在創建EnumSet時顯式或隱式地指定。
EnumSet在內部以位向量的形式存儲,十分緊湊、高效,因此EnumSet對象占用內存很小,且運行效率很高。但是該集合元素中不允許加入null元素。
實用類對比
分類 | HashSet | LinkedHashSet | TreeSet | EnumSet |
---|---|---|---|---|
實現形式 | Hash存儲 | 鏈表+Hash存儲 | 紅黑樹 | 枚舉類型(位向量) |
線程安全性 | 線程不安全 | 線程不安全 | 線程不安全 | 線程不安全 |
優點 | 插入和刪除速度快 | 集合元素有序,插入和刪除速度較快 | 集合元素有序,插入和刪除速度較快 | 批量操作速度較快,性能最好 |
缺點 | 集合元素無序 | (鏈表)性能較差 | (紅黑樹)性能較差 | 只能用來存儲枚舉類型 |
繼承與Collection接口的–Queue接口
Queue接口本身的特點
Queue用來模擬隊列這種數據結構,故其擁有add()、element()、offer()、peek()、poll()、remove()等方法。
package java.util; public interface Queue extends Collection { boolean add(E e); //在隊尾增加元素e boolean offer(E e); //在隊尾增加元素e E remove(); //獲取隊列頭部的元素,並移除該元素 E poll(); //獲取隊列頭部的元素,並移除該元素 E element(); //獲取隊列頭部的元素,但不移除該元素 E peek(); //獲取隊列頭部的元素,但不移除該元素 }
常見的繼承Queue接口的實用類
- PriorityQueue
PriorityQueue是Queue的標准隊列實現類。PriorityQueue不是按照加入隊列的順序進行排列的,而是根據隊列大小進行重新排序的(升序),這一點要特別注意的。
PriorityQueue中不允許插入null元素,它也有兩種排序方式:自然排序(實現Comparable接口,且為同一個類比較)和定制排序(建立對象時傳入一個Comparator對象)。這個部分和TreeSet的要求基本一致。 - Deque–ArrayDeque
首先要說明的是:Deque是Queue的子接口,代表的是雙向鏈表,故該接口中也定義了一些方法來從兩端來操作隊列的數據。
ArrayDeque:是一個基於數組實現的雙端隊列,它與ArratList的實現體制很相像,都會采用了動態可再分配的數組來存儲集合元素。但是其在用途上主要是被當作”棧“來使用。 - Deque–LinkedList
LinkedList在上述已表述過。與ArrayDeque不同的是,LinkedList是使用鏈表實現的。
特例獨立的–Map接口
Map接口本身的特點
Map與上述三個接口不同的是,該接口無法繼承於Collection接口,主要原因還是因為Map是用來存儲具有映射關系的數據,所以Map中保存着兩組值。一組值用來保存Map中的Key,另外一組用來保存Map里的value。值得注意的是:Map中的key不能重復。判斷標准是同一個Map對象的任何兩個key通過equals方法比較總是返回false。
package java.util; public interface Map { int size(); boolean isEmpty(); boolean containsKey(Object key); //是否包含key值,如果是返回true boolean containsValue(Object value); //是否包含value值,如果是返回true V get(Object key); //根據key值獲取相應的value值 V remove(Object key); //根據key值從集合中移除相應的value值 void putAll(Map<? extends K, ? extends V> m); //將指定的m中的key-value復制到該集合中 void clear(); Set<K> keySet(); //返回該Map中所有key組成Set集合 Collection<V> values(); //返回該Map中所有的value組成的Collection Set<Map.Entry<K, V>> entrySet(); //返回Map中包含的key-value對所組成的Set組合,每個集合元素都是Map.Entry對象。 interface Entry<K,V> { K getKey(); //返回該Entry里面包含的key值 V getValue(); //返回該Entry里面包含的value值 V setValue(V value); //設置原本的value值並返回該值 boolean equals(Object o); int hashCode(); } boolean equals(Object o); int hashCode(); }
Map與Set相似度極高,將Map中的key和value分離開來看。key組是使用set集合方法來存儲的。不僅如此,在子類的實現方面,兩者也有很多的相似,所以你也會發現兩者子類的命名很相像。
常見的繼承Map接口的實用類
- HashMap
HashMap是線程不安全的實現,且HashMap中可以使用null作為key或者value。 - LinkedHashMap
LinkedHashMap使用一個雙向鏈表來維護key-value對的次序,其也是一個有序的Map集合,順序與key-value對的插入順序保持一致。 - TreeMap
TreeMap是一個紅黑樹的結構,每個key-value作為紅黑樹的一個節點。TreeMap也會對key進行排序,也分為自然排序和定制排序兩種。 - EnumMap
EnumMap是一個與枚舉類一起使用的Map實現。 - HashTable
HashTable是一個比較古老的Map實現類,它是一個線程安全的Map實現,且不允許使用null作為key和value。由於該類是比較久遠的類,其性能較低,所以現在用的也比較少。
HashTable判斷value相等的標准是:value與另外一個對象通過equals()方法返回true即可。
實用類對比
一般的應用場景,使用HashMap已經夠用了。它非常適合於快速查詢。但是如果程序需要一個排序的Map集合,可以考慮使用TreeMap。
操作集合的工具類:Collections
Java提供了一個操作Set、List和Map等集合的工具類:Collections,該類提供了大量的方法對集合元素進行排序、查詢和修改等操作,還提供了將集合對象設置為不可變、對集合對象實現同步控制。
- 排序:reverse()(反轉集合元素順序)、shuffle()(隨機排列)、sort()、swap()等方法。
- 查找和替換:binarySearch(list,obj)(二分查找指定List集合元素obj的索引)、max()、min()、fill()、frequency()(返回指定集合元素的出現次數)、replaceAll()等方法。
- 同步控制:Collections提供了多個synchronizedXxx()方法,該方法可以將指定的集合包裝成線程同步的集合,進而解決了多線程並發訪問集合時的線程安全問題。
- 設置不可變集合:Collections提供三個方法來返回一個不可變的集合:emptyXxx()(返回一個空的、不可變的集合對象)、singletonXxx()、unmodifiableXxx()方法。