集合
集合:是一種容器,用來裝對象的容器,不能裝基本資料類型。
數組也是容器,可以用來裝基本資料類型,也可以用來裝對象。
本質上,集合需要用對應的資料結構實作,是多個類實作接口Collection系列和Map接口的統稱
Collection
Collection 表示一組對象,這些對象也稱為 collection 的元素。一些 collection 允許有重複的元素,而另一些則不允許。一些 collection 是有序的,而另一些則是無序的。
Collection<E>是所有單列集合的父接口,是以在Collection中定義了單列集合(List和Set)通用的一些方法,這些方法可用于操作所有的單列集合。
Collection方法:
序号 | 歸類 | 方法簽名 | 方法描述 |
---|---|---|---|
1 | 添加 | add(E e) | 添加一個元素對象到目前集合中 |
2 | addAll(Collection<? extends E> other) | 添加多個元素,把other集合的所有元素都添加到目前集合中,this = this ∪ other; | |
3 | 删除 | clear() | 清空集合 |
4 | remove(Object obj) | 删除一個元素,根據元素的equals()來判斷是否是要被删除的元素,如果元素的類型沒有重寫equals方法,那麼等價于==,如果重寫了equals,那麼就按照equals的規則來比較,一般比較内容。 | |
5 | removeAll(Collection<?> coll) | 删除多個元素,把目前集合中和c共同的元素删除,即this = this - this ∩ coll;子集 | |
6 | retainAll(Collection<?> coll) | 删除多個元素,在目前集合中保留和c的共同的元素,即this = this ∩ coll;交集 | |
7 | 查 | int size() | 擷取元素的個數 |
8 | boolean contains(Object obj) | 是否包含某個元素。根據元素的equals()來判斷是否是要被删除的元素,如果元素的類型沒有重寫equals方法,那麼等價于==,如果重寫了equals,那麼就按照equals的規則來比較,一般比較内容。 | |
9 | boolean containsAll(Collection<?> coll) | 是否包含多個元素。判斷目前集合中是否包含coll集合的所有元素,即coll是否是this的子集。 | |
10 | boolean isEmpty() | 集合是否為空 | |
11 | 周遊 | Object[] toArray() | 将集合中的元素用數組傳回 |
12 | Iterator iterator() | 傳回一個疊代器對象,專門用于周遊集合 |
Iterator疊代器
即Collection集合元素的通用擷取方式。每一種實作了Iterable接口的集合内部,都會有一個内部類實作了Iterator接口
-
【如果仍有元素可以疊代,則傳回 true。】public boolean hasNext()
-
【傳回疊代的下一個元素。】public E next()
- void remove() 【使用Iterator疊代器删除元素】

在調用Iterator的next方法之前,疊代器的索引位于第一個元素之前,指向第一個元素,當第一次調用疊代器的next方法時,傳回第一個元素,然後疊代器的索引會向後移動一位,指向第二個元素,依此類推,直到hasNext方法傳回false,表示到達了集合的末尾,終止對元素的周遊。
在進行集合元素取出時,如果集合中已經沒有元素了,還繼續使用疊代器的next方法,将會發生java.util.NoSuchElementException沒有集合元素的錯誤
注意:不要在使用Iterator疊代器進行疊代時,調用Collection的remove(xx)方法,否則會報異常java.util.ConcurrentModificationException,或出現不确定行為。
增強for
增強for循環(也稱for each循環)是JDK1.5以後出來的一個進階for循環,專門用來周遊數組和集合的,或者說實作了Iterable接口的其他容器名。
for(元素的資料類型 變量 : Collection集合or數組){
//寫操作代碼
}
Iterable接口
java.lang.Iterable接口,實作這個接口允許對象成為 "foreach" 語句的目标。
java.lang.Iterable接口的抽象方法:
- public Iterator iterator(): 【擷取對應的疊代器】
foreach本質上就是使用Iterator疊代器進行周遊的。
是以也不要在foreach周遊的過程使用Collection的remove()方法。
modCount
如果在Iterator、ListIterator疊代器建立後的任意時間從結構上修改了集合(通過疊代器自身的 remove 或 add 方法之外的任何其他方式),則疊代器将抛出 ConcurrentModificationException。這就是Iterator疊代器的快速失敗(fail-fast)機制。
結構性修改是指:改變list的size大小,或者,以其他方式改變他導緻正在進行疊代時出現錯誤的結果。
那麼如何實作快速失敗(fail-fast)機制的呢?
- 在ArrayList等集合類中都有一個modCount變量。它用來記錄集合的結構被修改的次數。
- 當我們給集合添加和删除操作時,會導緻modCount++。
- 然後當我們用Iterator疊代器周遊集合時,建立集合疊代器的對象時,用一個變量記錄目前集合的modCount。例如:
,并且在疊代器每次next()疊代元素時,都要檢查int expectedModCount = modCount;
expectedModCount != modCount
注意,疊代器的快速失敗行為不能得到保證,是以,編寫依賴于此異常的程式的方式是錯誤的,正确做法是:疊代器的快速失敗行為應該僅用于檢測 bug。
自定義類時如果一個實作類不希望提供fail-fast疊代器,則可以忽略這個字段。
List
List接口特點:
- 它是一個元素存取有序的集合。即元素的存入順序和取出順序有保證。
- 它是一個帶有索引的集合,通過索引就可以精确的操作集合中的元素(與數組的索引是一個道理)。
- 集合中可以有重複的元素,通過元素的equals方法,來比較是否為重複的元素。
List集合關心元素是否有序,而不關心是否重複
List接口的實作類有很多,常見的有:
ArrayList:動态數組 Vector:動态數組 LinkedList:雙向連結清單 Stack:棧
List除了從Collection集合繼承的方法外,List 集合裡添加了一些根據索引來操作集合元素的方法。
1、添加元素
- void add(int index, E ele) 【在[index]位置添加一個元素】
- boolean addAll(int index, Collection<? extends E> eles) 【在[index]位置添加多個元素】
2、擷取元素
- E get(int index) 【傳回[index]位置的元素】
- List subList(int fromIndex, int toIndex) 【截取[fromIndex,toIndex)部分的元素】
3、擷取元素索引
- int indexOf(Object obj) 【傳回obj在目前集合中第一次出現的下标】
- int lastIndexOf(Object obj) 【傳回obj在目前集合中最後一次出現的下标】
4、删除和替換元素
- E remove(int index) 【删除[index]位置的元素,傳回被删除的元素】
- E set(int index, E ele) 【替換[index]位置的元素,傳回被替換的元素】
5、周遊
在原來Iterator和foreach周遊的基礎上增加了:
ListIterator listIterator() 【預設遊标在[0]開始】
ListIterator listIterator(int index) 【預設遊标在[index]位置】
ListIterator
List 集合額外提供了一個 listIterator() 方法,該方法傳回一個 ListIterator 對象, ListIterator 接口繼承了 Iterator 接口,提供了專門操作 List 的方法:
- void add(): 【通過疊代器添加元素到對應集合】
- void set(E e): 【用指定元素替換 next 或 previous 傳回的最後一個元素】
- void remove(): 【從清單中移除由 next 或 previous 傳回的最後一個元素】
- boolean hasPrevious(): 【如果以逆向周遊清單,往前是否還有元素。則傳回 true】
- E previous(): 【傳回清單中的前一個元素。】
- int previousIndex(): 【傳回清單中的前一個元素的索引】
- boolean hasNext() 【以正向周遊清單時,如果清單疊代器有多個元素,則傳回 true】
- E next() 【傳回清單中的下一個元素。】
- int nextIndex() 【傳回對 next 的後續調用所傳回元素的索引。】
Set
set接口沒有提供額外的方法。但是比
Collection
接口更加嚴格了。
Set 集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個 Set 集合中,則添加操作失敗。
Set集合支援的周遊方式和Collection集合一樣:foreach和Iterator。
Set集合的實作類:TreeSet:按大小順序,LinkedHashSet:按照添加的順序,HashSet:無序
HashSet
HashSet 是 Set 接口的典型實作,大多數時候使用 Set 集合時都使用這個實作類。
java.util.HashSet
底層的實作其實是一個
java.util.HashMap
支援,然後HashMap的底層實體實作是一個Hash表。
HashSet 集合判斷兩個元素相等的标準:兩個對象通過 hashCode() 方法比較相等,并且兩個對象的 equals() 方法傳回值也相等。是以,存儲到HashSet的元素要重寫hashCode和equals方法。
LinkedHashSet
LinkedHashSet是HashSet的子類,它在HashSet的基礎上,在結點中增加兩個屬性before和after維護了結點的前後添加順序。
java.util.LinkedHashSet
,它是連結清單和哈希表組合的一個資料存儲結構。LinkedHashSet插入性能略低于 HashSet,但在疊代通路 Set 裡的全部元素時有很好的性能。
HashSet/LinkedHashSet:如何差別元素的不可重複。依賴于元素的hashCode和equals方法
TreeSet
底層結構:裡面維護了一個TreeMap,都是基于紅黑樹實作的!
特點: 1、不允許重複 2、實作排序, 自然排序或定制排序
如果使用的是自然排序(Comparable),則通過調用實作的compareTo方法
自然排序:它判斷兩個對象是否相等的唯一标準是:兩個對象通過 compareTo(T o) 方法比較傳回值為0。
如果使用的是定制排序(Comparator),則通過調用比較器的compare方法
定制排序:使用定制排序判斷兩個元素相等的标準是:通過compare(T o1,T o2)比較兩個元素傳回了0。
如果希望保持一緻性,在重寫compareTo時,一般也會重寫equals方法。不是文法要求,而且邏輯意義問題。
Collection系列的集合架構圖
Map
Collection
中的集合稱為單列集合,
Map
中的集合稱為雙列集合。java.util.Map<K,V>
1、存儲鍵值對(key,value),也稱為映射關系,鍵值對是Map.Entry接口的實作類對象。
2、所有存儲到Map中的key不能重複, 每個鍵隻能對應一個值(這個值可以是單個值,也可以是個數組或集合值)。
3、所有存儲到Map中的value可以重複
Map接口的API
1、添加
V put(K key, V value): 【将一對鍵值對添加到目前map中,同一個key如果put兩次,第二次會覆寫上次的value】
void putAll(Map m): 【将另一個map中的所有鍵值對添加到目前map中】
2、删除
void clear(): 【清空所有映射關系】
V remove(Object key): 【根據key删除一整對鍵值對(key,value)】
3、查詢
int size(): 【傳回鍵值對的數量】
boolean containsKey(Object key): 【是否包含某個key】
boolean containsValue(Object value): 【是否包含某個value】
V get(Object key): 【根據key擷取value值,如果此映射不包含該鍵的映射關系,則傳回
null
。】
boolean isEmpty() 【如果此映射未包含鍵-值映射關系,則傳回 true。】
4、周遊
Set<Entry<K,V>> entrySet() 【周遊所有的鍵值對,映射關系的
Set
視圖 】
Set<K> keySet() 【周遊所有的key,鍵的
Set
視圖】
Collection<V> values() 【周遊所有的value,值的
Collection
使用put方法時,若指定的鍵(key)在集合中沒有,則沒有這個鍵對應的值,傳回null,并把指定的鍵值添加到集合中;
若指定的鍵(key)在集合中存在,則傳回值為集合中鍵對應的值(該值為替換前的值),并把指定鍵所對應的值,替換成指定的新值。
Map集合的周遊
Map的周遊,不能支援foreach
(1)分開周遊:
- 單獨周遊所有key
- 單獨周遊所有value
(2)成對周遊:
- 周遊的是映射關系Map.Entry類型的對象,Map.Entry是Map接口的内部接口。每一種Map内部有自己的Map.Entry的實作類。在Map中存儲資料,實際上是将Key---->value的資料存儲在Map.Entry接口的執行個體中,再在Map集合中插入Map.Entry的執行個體化對象
Map接口的常用實作類:HashMap、TreeMap、LinkedHashMap和Properties。其中HashMap是 Map 接口使用頻率最高的實作類。
HashMap和Hashtable的差別與聯系
- HashMap和Hashtable都是哈希表。
- HashMap和Hashtable判斷兩個 key 相等的标準是:兩個 key 的hashCode 值相等,并且 equals() 方法也傳回 true。是以,為了成功地在哈希表中存儲和擷取對象,用作鍵的對象必須實作 hashCode 方法和 equals 方法。
- Hashtable是線程安全的,任何非 null 對象都可以用作鍵或值。不允許null鍵
- HashMap是線程不安全的,并允許使用 null 值和 null 鍵。
LinkedHashMap
LinkedHashMap 是 HashMap 的子類。LinkedHashMap實作與 HashMap 的不同之處在于,LinkedHashMap維護着一個運作于所有條目的雙重連結清單。此連結清單定義了疊代順序,該疊代順序通常就是将鍵插入到映射中的順序(插入順序)。
TreeMap
基于紅黑樹(Red-Black tree)的 NavigableMap 實作。該映射根據其鍵的自然順序進行排序,或者根據建立映射時提供的 Comparator 進行排序,具體取決于使用的構造方法。
Properties
Properties 類是 Hashtable 的子類,Properties 可儲存在流中或從流中加載。屬性清單中每個鍵及其對應值都是一個字元串。
存取資料時,建議使用setProperty(String key,String value)方法和getProperty(String key)方法。
更多方法請見API文檔
Set集合與Map集合的關系
Set的内部實作其實是一個Map。即HashSet的内部實作是一個HashMap,TreeSet的内部實作是一個TreeMap,LinkedHashSet的内部實作是一個LinkedHashMap。
咱們存到Set中隻有一個元素,又是怎麼變成(key,value)的呢?
原來是,把添加到Set中的元素作為内部實作map的key,然後用一個常量對象PRESENT對象,作為value。