Java語法進階12-集合
集合
集合:是一種容器,用來裝物件的容器,不能裝基本資料型別。
陣列也是容器,可以用來裝基本資料型別,也可以用來裝物件。
本質上,集合需要用對應的資料結構實現,是多個類實現介面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介面
-
public boolean hasNext()
【如果仍有元素可以迭代,則返回 true。】 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。例如:
int expectedModCount = modCount;
,並且在迭代器每次next()迭代元素時,都要檢查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。