集合:集合是Java中提供的一種容器,可以用來存儲多個數據。
集合和數組的區別:
(1)數組長度的是固定的,集合的長度是可變的。
(2)數組中存儲的都是同一類型的元素。集合存儲的都是對象,對象的類型可以不一致。
Java集合類主要由兩個根接口Collection和Map派生出來的。Collection有三個子接口: List、Set、Queue(Java5新增的隊列)。Java集合大致也可分成List、Set、Queue、Map四種接口體系,注意:Map不是Collection的子接口。
Map接口:雙列數據,保存具有映射關系“key-value對”
Collection接口
Collection接口:單列數據,定義了存取一組對象的方法的集合。
Collection接口是List、Set和Queue接口的父接口,該接口里定義的方法既可用於操作Set集合,也可用於操作List和 Queue集合。
Collection接口的常用方法
Collection子接口之一:List接口
List集合類中元素有序、且可重復,集合中的每個元素都有其對應的順序索引。List集合默認按照元素的添加順序設置元素的索引,可以通過索引(類似數組的下標)來訪問指定位置的集合元素。
List接口的實現類主要有:ArrayList、LinkedList和Vector。
List接口的常用方法
返回值類型 | 方法名及描述 |
---|---|
boolean | add(E e) 將指定的元素追加到此列表的末尾(可選操作)。 |
void | add(int index, E element) 將指定的元素插入此列表中的指定位置(可選操作)。 |
boolean | addAll(Collection c) 按指定集合的迭代器(可選操作)返回的順序將指定集合中的所有元素附加到此列表的末尾。 |
boolean | addAll(int index, Collection c) 將指定集合中的所有元素插入到此列表中的指定位置(可選操作)。 |
void | clear() 從此列表中刪除所有元素(可選操作)。 |
boolean | contains(Object o) 如果此列表包含指定的元素,則返回true。 |
E | get(int index) 返回此列表中指定位置的元素。 |
int | hashCode() 返回此列表的哈希碼值。 |
int | indexOf(Object o) 返回此列表中指定元素的第一次出現的索引,如果此列表不包含元素,則返回-1。 |
boolean | isEmpty() 如果此列表不包含元素,則返回 true 。 |
Iterator | iterator() 以正確的順序返回該列表中的元素的迭代器。 |
int | lastIndexOf(Object o) 返回此列表中指定元素的最后一次出現的索引,如果此列表不包含元素,則返回-1。 |
E | remove(int index) 刪除該列表中指定位置的元素(可選操作)。 |
E | set(int index, E element) 用指定的元素(可選操作)替換此列表中指定位置的元素。 |
int | size() 返回此列表中的元素數。 |
(1)List接口的實現類之一:ArrayList
ArrayList的JDK1.8之前與之后的實現區別:
JDK1.7:直接創建一個初始容量為10的數組
JDK1.8:一開始先創建一個長度為0的數組,當添加第一個元素時再創建一個初始容量為10的數組
(2)List接口的實現類之二:LinkedList
LinkedList是List接口的另一個實現,除了可以根據索引訪問集合元素外,LinkedList還實現了Deque接口。
LinkedList內部以鏈表的形式保存集合中的元素,所以隨機訪問集合中的元素性能較差,但在頻繁的插入或刪除元素時有較好的性能。
(3)List接口的實現類之三:Vector
Vector大多數操作與ArrayList相同,區別之處在於Vector是線程安全的。
面試題:
ArrayList和LinkedList的區別
都是線程不安全,相對線程安全的Vector,執行效率高。此外,ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指針。對於新增和刪除操作add(特指插入)和remove,LinkedList比較占優勢,因為ArrayList要移動數據。
ArrayList和Vector的區別
Vector和ArrayList幾乎是完全相同的,唯一的區別在於Vector是同步類(synchronized),屬於強同步類。因此開銷就比ArrayList要大,訪問要慢。大多數清空下使用 ArrayList而不是Vector,因為同步完全可以由自己來控制。Vector每次擴容請求其大小的2倍空間,而ArrayList是1.5倍。Vector還有一個子類Stack。
Collection子接口之二:Set接口
Set接口也是Collection的子接口,set接口沒有提供額外的方法。
Set集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個Set集合中,則會添加操作失敗。
Set集合判斷兩個對象是否相同是根據 equals() 方法,而不是使用 == 運算符。
(1)Set接口的實現類之一:HashSet
HashSet是Set接口的典型實現,大多數時候使用Set集合時都使用這個實現類。
HashSet按Hash算法來存儲集合中的元素,因此具有很好的存取、查找、刪除性能。
HashSet 具有以下特點:
-
不能保證元素的排列順序
-
HashSet不是線程安全的
-
集合元素可以是null
HashSet集合判斷兩個元素相等的標准:兩個對象通過 hashCode() 方法比較相等,並且兩個對象的 equals() 方法返回值也相等。
對於存放在Set容器中的對象,對應的類一定要重寫equals()和hashCode(Object obj)方法,以實現對象相等規則。
HashSet添加元素的原理如下:
當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然后根據 hashCode值,通過某種散列函數決定該對象在HashSet底層數組中的存儲位置。
如果兩個元素的hashCode()值相等,會再繼續調用equals方法,如果equals方法結果為true,則添加失敗。如果為false,那么會保存該元素,但是該數組的位置已經有元素了, 那么會通過鏈表的方式繼續鏈接存儲。
如果兩個元素的equals() 方法返回true,但它們的 hashCode() 返回值不相等,hashSet將會把它們存儲在不同的位置,但依然可以添加成功。
重寫hashCode() 方法的基本原則
在程序運行時,同一個對象多次調用 hashCode() 方法應該返回相同的值
當兩個對象通過equals()方法比較返回true時,這兩個對象的hashCode()方法的返回值應該相等。
對象中用作equals()方法比較的實例變量,都應該用來計算hashCode值。
(2)Set接口的實現類之二:LinkedHashSet
LinkedHashSet是HashSet的子類。
LinkedHashSet也是根據元素的hashCode值來決定元素的存儲位置。但它同時使用雙向鏈表維護元素的次序,元素的順序與添加順序一致。
由於LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet,但在迭代訪問Set里的全部元素時有很好的性能。
LinkedHashSet不允許集合元素重復。
(3)Set接口的實現類之三:TreeSet
TreeSet是SortedSet接口的實現類,TreeSet可以確保集合元素處於排序狀態。
TreeSet底層使用紅黑樹結構存儲數據元素。
TreeSet兩種排序方法:自然排序和定制排序。默認情況下,TreeSet采用自然排序。
自然排序:
TreeSet會調用集合元素的compareTo(Object obj) 方法來比較元素之間的大小關系,然后將集合元素按升序(默認情況)排列。
如果試圖把一個對象添加到TreeSet時,則該對象的類必須實現Comparable接口。
實現Comparable的類必須實現compareTo(Object obj) 方法,兩個對象即通過compareTo(Object obj) 方法的返回值來比 較大小。
Comparable的一些典型實現類:
-
BigDecimal、BigInteger 以及所有的數值型對應的包裝類:按它們對應的數值大小進行比較
-
Character:按字符的 unicode值來進行比較
-
Boolean:true 對應的包裝類實例大於 false 對應的包裝類實例
-
String:按字符串中字符的 unicode 值進行比較
-
Date、Time:后邊的時間、日期比前面的時間、日期大
因為只有相同類的兩個實例才會比較大小,所以向TreeSet中添加的應該是同一個類的對象。
對於TreeSet 集合而言,它判斷兩個對象是否相等的唯一標准是:兩個對象通過compareTo(Object obj) 方法比較返回值。
當需要把一個對象放入TreeSet 中,重寫該對象對應的equals() 方法時,應保證該方法與compareTo(Object obj) 方法有一致的結果。
對於TreeSet集合而言,它判斷兩個對象是否相等的標准是:兩個對象通過compareTo(Object obj)方法比較是否返回0,如果返回0則相等。
定制排序
TreeSet的自然排序要求元素所屬的類實現Comparable接口,如果元素所屬的類沒有實現Comparable接口,或不希望按照升序(默認情況)的方式排列元素,希望按照其它屬性大小進行排序,則考慮使用定制排序。定制排序,通過Comparator接口來實現。需要重寫compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大於o2。
要實現定制排序,需要將實現Comparator接口的實例作為形參傳遞給TreeSet的構造器。
使用定制排序判斷兩個元素相等的標准是:通過Comparator比較兩個元素返回了0。
Map接口
Map與Collection並列存在。用於保存具有映射關系的數據:key-value
Map中的key和value都可以是任何引用類型的數據
Map中的key用Set來存放,不允許重復,即同一個Map對象所對應的類,須重寫hashCode()和equals()方法
Map接口的常用實現類:HashMap、TreeMap、LinkedHashMap和Properties
(1)Map接口的實現類之一:HashMap
HashMap是Map接口使用頻率最高的實現類。允許使用null鍵和null值,與HashSet一樣,不保證映射的順序。
所有的key構成的集合是Set:無序的、不可重復的。所以,key所在的類要重寫:equals()和hashCode()
所有的value構成的集合是Collection:無序的、可以重復的。所以,value所在的類要重寫:equals()
HashMap判斷兩個key相等的標准是:兩個key通過equals() 方法返回true, hashCode值也相等。
HashMap判斷兩個value相等的標准是:兩個value通過equals()方法返回true。
HashMap可以使用null值為key或value
HashMap源碼中的重要常量
DEFAULT_INITIAL_CAPACITY: HashMap的默認容量,16
MAXIMUM_CAPACITY: HashMap的最大支持容量,2^30
TREEIFY_THRESHOLD:Bucket中鏈表長度大於該默認值,轉化為紅黑樹。
UNTREEIFY_THRESHOLD:Bucket中紅黑樹存儲的Node小於該默認值,轉化為鏈表
table:存儲元素的數組,總是2的n次冪
entrySet:存儲具體元素的集
size:HashMap中存儲的鍵值對的數量
modCount:HashMap擴容和結構改變的次數。
HashMap的存儲結構
JDK8之前版本:
JDK8之前HashMap的存儲結構是數組+鏈表結構(即為鏈地址法) ,當實例化一個HashMap時,系統會創建一個長度為Capacity的Entry數組,這個長度在哈希表中被稱為容量 (Capacity),在這個數組中可以存放元素的位置我們稱之“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
每個bucket中存儲一個元素,即一個Entry對象,但每一個Entry對象可以帶一個引用變量,用於指向下一個元素,因此,在一個桶中,就有可能生成一個Entry鏈。 而且新添加的元素作為鏈表的head。
JDK8之后版本:
JDK8之后HashMap的存儲結構是數組+鏈表+紅黑樹實現。當實例化一個HashMap時,會初始化initialCapacity和loadFactor,在put第一對映射關系時,系統會創建一個長度為initialCapacity的Node數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為 “桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
每個bucket中存儲一個元素,即一個Node對象,但每一個Node對象可以帶一個引用變量next,用於指向下一個元素,因此,在一個桶中,就有可能生成一個Node鏈。也可能是一個一個TreeNode對象,每一個TreeNode對象可以有兩個葉子結點left和right,因此,在一個桶中,就有可能生成一個TreeNode樹。而新添加的元素作為鏈表的last,或樹的葉子結點。
(2)Map接口的實現類之二:LinkedHashMap
LinkedHashMap 是 HashMap的子類,在HashMap存儲結構的基礎上,使用了一對雙向鏈表來記錄添加元素的順序。 該鏈表負責維護Map的迭代順序,與插入順序一致,因此性能比HashMap低,但在迭代訪問Map里的全部元素時有較好的性能。
(3)Map接口的實現類之三:TreeMap
TreeMap存儲Key-Value對時,需要根據key-value對進行排序。TreeMap可以保證所有的Key-Value對處於有序狀態。底層采用紅黑樹的數據結構。
TreeMap也有兩種排序方式,自然排序和定制排序。
(4)Map接口的實現類之四:Hashtable
HashMap線程不安全,Hashtable是線程安全的。
HashMap可以使用null值為key或value,Hashtable不允許使用null作為key和value。
Hashtable實現原理和HashMap相同,底層都使用哈希表結構,查詢速度快。
Hashtable和HashMap一樣也不能保證其中Key-Value對的順序。
(5)Map接口的實現類之五:Properties
Properties類是Hashtable 的子類,該對象用於處理屬性文件。
由於屬性文件里的key、value都是字符串類型,所以Properties里的key和value都是字符串類型 。
Collections工具類
Collections是一個操作 Set、List 和 Map 等集合的工具類。
Collections 中提供了一系列靜態的方法對集合元素進行排序、查詢和修改等操作, 還提供了對集合對象設置不可變、對集合對象實現同步控制等方法。
Iterator迭代器接口
Iterator接口用來遍歷集合元素
Collection接口繼承了java.lang.Iterable接口,該接口有一個iterator()方法,所有實現了Collection接口的集合類都有一個iterator()方法,用以返回一個實現了 Iterator接口的對象。
集合對象每次調用iterator()方法都得到一個全新的迭代器對象。