Java(6)集合
一、Java集合框架概述
1、什麼是集合
- 集合框架:用於儲存資料的容器。
- 陣列、集合等儲存資料的結構,叫Java容器。
- 此時的儲存,是指記憶體層面的儲存,不涉及持久化的儲存。
- 任何集合框架都包含三大塊的內容:對外的介面、介面的實現、對集合運算的演算法。
2、集合的特點
-
陣列的特點/缺點:
- 長度固定。一旦初始化,長度不能修改。
- 型別確定。型別嚴格(算是一個好處),當然要想可以放多種型別的資料,也可以宣告為Object型別。
- 方法有限。新增、刪除元素效率低;獲取元素個數不方便。
- 元素有序可重。對於無序、不重複的元素,不能滿足。
-
集合的特點/優點
- 容量自增長。
3、集合的體系
- Collection介面和Map介面是所有集合框架的父介面。
- Collection介面繼承樹
說明:
- List介面:儲存有序、可重複的資料。“動態陣列”
- Set介面:儲存無序、不可重複的資料。“數學中的集合”
- Map介面繼承樹
說明:
- Map介面:雙列集合,用於儲存成對的資料(key-value)。“數學中的函式”
二、Collection介面中的方法(15個)
1、說明
- 因為Collection介面是List、Set、Queue介面的父介面,所以定義在Collection介面的中方法可以用於操作子介面的實現類的物件。
2、15個方法
方法 | 描述 |
---|---|
add(Object obj) | 新增元素 |
addAll(Collection c) | 新增另一個集合中所以元素 |
size() | 元素個數 |
clear() | 清空集合 |
isEmpty() | 是否為空 |
contains(Object obj) | 包含某個元素 |
containsAll(Collection c) | 包含某個集合中所有元素 |
remove(Object obj) | 刪除元素 |
removeAll(Collection c) | 差集 |
retainAll(Collection c) | 交集 |
equals(Object obj) | 判斷集合是否相等 |
toArray() | 轉換為陣列 |
toArray(T[] a) | |
hashCode() | 求集合的雜湊值 |
iterator() | 返回迭代器物件 |
注意事項:
-
想Collection介面的實現類物件中新增資料obj時,要求obj所在類重寫equals()。
-
陣列--->集合:Arrays.asList()
-
List arr1 = Arrays.asList(new int[]{123, 456}); System.out.println(arr1.size());//1
-
List arr2 = Arrays.asList(new Integer[]{123, 456}); System.out.println(arr2.size());//2
-
asList中是整型時,用int,預設會把陣列當場一個物件。
-
3、Iterator迭代器介面
- Iterator是什麼?
- Iterator物件成為迭代器,是設計模式的一種。主要用於遍歷集合中的元素。
- 迭代器模式:提供一種方法訪問一個容器(container)物件中各個元 素,而又不需暴露該物件的內部細節。迭代器模式,就是為容器而生。
- Iterator介面中方法
- hasNext()
- next()
- remove()遍歷過程中移除某元素
- Iterator可以刪除集合的元素,但是是遍歷過程中通過迭代器物件的 remove方 法,不是集合物件的remove方法。
- 如果還未呼叫next()或在上一次呼叫 next 方法之後已經呼叫了 remove 方法, 再呼叫remove都會報IllegalStateException
- 怎麼用Iterator遍歷集合
//用集合中的iterator方法得到迭代器物件
Iterator iterator = coll.iterator();
//遍歷集合
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//當然我們也可以用foreach來遍歷,其實foreach本質也是呼叫了迭代器。
-
Iterator原理
- 得到的迭代器物件,預設指標指向第一個元素的前面
- hasNext()來判斷下一位是否有元素。(在呼叫it.next()方法之前必須要呼叫it.hasNext()進行檢測。若不呼叫,且 下一條記錄無效,直接呼叫it.next()會丟擲NoSuchElementException異常)
- next():兩個作用:①指標下移②取出指標指向的元素
-
注意
- 集合物件每次呼叫iterator()方法都得到一個全新的迭代器物件,預設遊標都在集合 的第一個元素之前。因此下面這種寫法不正確
while (coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
三、List介面
1、List介面概述
- List介面是Collection的子介面
- List被稱為“動態陣列”,是因為陣列的侷限性而代替陣列的一種容器。
- 儲存的元素有序、可重複。每個元素都有對應的順序索引。
- 實現類有三個:ArrayList、LinkedList、Vector
2、ArrayList、LinkedList、Vector的異同(面試題)
- 同
- 三個類都是List介面的實現類,因此儲存資料都是有序可重複的。
- 異
實現類 | 地位 | since | 執行緒安全否 | 底層 | 應用場景 |
---|---|---|---|---|---|
ArrayList | 主要實現類 | 1.2 | 執行緒不安全、效率高 | Object[] | 遍歷、查詢 |
LinkedList | 1.2 | 雙向連結串列 | 頻繁插入、刪除 | ||
Vector | 古老實現類 | 1.0 | 執行緒安全、效率低 | Object[] |
3、原始碼分析(加分項)
-
ArrayList原始碼分析-----JDK7
- 建立:呼叫空參構造器時,底層建立一個長度為10的Object[]陣列elementData
- 擴容:預設擴容為原來的1.5倍(使用移位操作),並將原來陣列中的資料複製到新的陣列中
- 結論:開發中使用帶參構造器指定容量:
ArrayList list = new ArrayList(int capacity)
-
ArrayList原始碼分析-----JDK8
- 建立:呼叫空參構造器時,底層建立一個Object[]陣列elementData,初始化為空{}
- 擴容:同JDK7
- 對比:jdk7中的ArrayList的物件的建立類似於單例的餓漢式,而jdk8中的ArrayList的物件的建立類似於單例的懶漢式,延遲了陣列的建立,節省記憶體。
-
LinkedList原始碼分析
- 建立:呼叫空參構造器時,內部聲明瞭Node型別的first和last屬性,預設值為null
- 新增:將資料封裝在Node中,建立Node物件
-
Vector原始碼分析
- 建立:呼叫空參構造器時,底層建立一個長度為10的Object[]陣列elementData
- 擴容:預設擴容為原來的2倍,並將原來陣列中的資料複製到新的陣列中
4、List常用方法
-
List除了從Collection集合繼承的方法外,還有一些獨有的、根據索引來操作集合元素的方法。
-
總結的簡化版(常用的、便於記憶的總結)
常用方法的作用 | 方法名 |
---|---|
增 | add(Object obj) |
刪 | remove(Object obj)、remove(int index) |
改 | set(int index, Object obj) |
查 | get(int index) |
插 | add(int index, Object obj) |
長度 | size() |
遍歷 | ①、Iterator迭代器 ②、增強for迴圈 ③、普通for迴圈 |
- 區分List中的remove方法
- list.remove(2):刪除索引為2的,因為有remove(int index)的方法,直接匹配上不用自動裝箱就能匹配,name何必自動裝箱呢?
- list.remove(new Integer(2)):刪除資料2,匹配的是remove(Object obj)的方法
四、Set介面
1、Set介面概述
- Set介面是Collection的子介面。
- Set中沒有額外的方法
- Set中的元素不可重複,判斷是否相等用的是equals()方法
- 實現類有HashSet、LinkedHashSet、TreeSet
2、HashSet、LinkedHashSet、TreeSet的異同
- HashSet:是Set介面的主要實現類;執行緒不安全;可以儲存null值;
- LinkedHashSet:是HashSet的子類;可以按照新增順序遍歷,遍歷效率高;
- TreeSet:可以按照新增物件的指定屬性排序
- 要求1:新增的物件型別相同
- 要求2:物件的類要實現Comparable介面來進行自然排序,或者,實現Comparator介面來進行定製排序
- 自然排序中,比較兩個物件是否相同的標準為(是否可新增“相同物件”):compareTo()返回0.不再是equals().
- 定製排序中,比較兩個物件是否相同的標準為:compare()返回0.不再是equals().
3、理解無序、可重複
- 無序
- 指的是儲存在底層陣列的資料,並不是按照陣列索引的順序新增的,而是根據資料的雜湊值決定的。
- 無序性,不等於隨機性
- 不可重複
- 相同的元素只能新增一個。元素按照equals()判斷,不能返回true。
4、新增元素的過程(以HashSet為例)
-
新增過程並不會單獨考察,但是對於理解HashMap有很大幫助
-
我們向HashSet中新增元素a,首先呼叫元素a所在類的hashCode()方法,計算元素a的雜湊值,此雜湊值接著通過某種演算法計算出在HashSet底層陣列中的存放位置(即為:索引位置),判斷陣列此位置上是否已經有元素:
- 如果此位置上沒有其他元素,則元素a新增成功。 --->情況1
- 如果此位置上有其他元素b(或以連結串列形式存在的多個元素),則比較元素a與元素b的hash值:
- 如果hash值不相同,則元素a新增成功。--->情況2
- 如果hash值相同,進而需要呼叫元素a所在類的equals()方法:
- equals()返回true,元素a新增失敗
- equals()返回false,則元素a新增成功。--->情況3
-
對於新增成功的情況2和情況3而言:元素a 與已經存在指定索引位置上資料以連結串列的方式儲存
- jdk 7 :元素a放到陣列中,指向原來的元素。
- jdk 8 :原來的元素在陣列中,指向元素a
- 總結:七上八下
-
HashSet底層:陣列+連結串列的結構。
5、重寫hashCode()和equals()方法
-
要求:向Set(主要指:HashSet、LinkedHashSet)中新增的資料,其所在的類一定要重寫hashCode()和equals()
-
重寫要求:重寫的hashCode()和equals()儘可能保持一致性:相等的物件必須具有相等的雜湊碼
-
重寫兩個方法的小技巧:物件中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值。
6、面試題
-
在List內去除重複數字值,要求儘量簡單
- 可以用Set實現
-
//程式的輸出結果 HashSet set = new HashSet(); Person p1 = new Person(1001,"AA"); Person p2 = new Person(1002,"BB"); set.add(p1); set.add(p2); p1.name = "CC"; set.remove(p1); System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}] /* 存放p1、p2時,根據p1和p2的屬性算出了hashCode來確定存放位置 p1的name改為CC後,再刪除p1--->根據此時p1的屬性算hashCode來找set中和p1相同的元素,此時算出來的hashCode大概率不是p1的位置,相當於沒有找到,所以刪除並沒有成功。 */ set.add(new Person(1001,"CC")); System.out.println(set); //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}] /* 存放新建的p,是根據1001和CC來算hashCode,此時陣列中這個位置空的,所以存放成功 */ set.add(new Person(1001,"AA")); System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}] /* 存放新建的p,先hashCode後和p1是一樣的,再equals發現不一樣,加到連結串列上,存放成功 */
五、Map介面
1、Map介面概述
- Map介面繼承樹
- Map介面:
- 儲存key-value的鍵值對,類似於數學中的函式
- Map實現類對比
Map實現類 | 底層實現 | 執行緒 | 地位 | 特點 |
---|---|---|---|---|
HashMap | JDK7及以前:陣列+連結串列 JDK8:陣列+連結串列+紅黑樹 |
執行緒不安全 | 主要實現類 | 可以儲存null的k-v |
LinkedHashMap | 同上 | 同上 | 同上 | 可按照新增順序遍歷 |
TreeMap | --- | --- | --- | 可排序遍歷 |
Hashtable | --- | 執行緒安全 | 古老實現類 | 不可儲存null的k-v |
Properties | --- | --- | --- | 常用來處理配置檔案 |
2、面試題
-
HashMap的底層實現原理★★★★★(見下面第四點)
-
HashMap和Hashtable的異同
-
CurrentHashMap和Hashtable的異同(暫時不學)
3、Map結構的理解
-
Map中的key:無序的、不可重複的,使用Set儲存所有的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例);當然如果是LinkedHashMap還要實現排序介面
-
Map中的value:無序的、可重複的,使用Collection儲存所有的value --->value所在的類要重寫equals()
-
一個鍵值對:key-value構成了一個Entry物件。
-
Map中的entry:無序的、不可重複的,使用Set儲存所有的entry
四、 HashMap的底層實現原理★★★★★(高頻面試題)
1、JDK7
-
HashMap map = new HashMap()
:底層建立一個長度為16的陣列Entry[] table。 -
map.put(key1, value1)
- 呼叫key1所在類的hashCode()方法計算key1的雜湊值,用雜湊值在計算得到在Entry[]陣列中的存放位置
- 如果此位置為空,新增成功------>情況1
- 如果此位置不為空,比較key1和已經存在的資料的雜湊值
- 如果key1的雜湊值和已經存在的資料的雜湊值不同,新增成功------>情況2
- 如果key1的雜湊值和已經存在的某一個數據(key2,value2)的雜湊值相同,繼續呼叫key1所在類的equals()方法,傳入引數key2進行比較
- 如果equals()返回值為false,新增成功------>情況3
- 如果equals()返回值為true,用value1替換value2
- 呼叫key1所在類的hashCode()方法計算key1的雜湊值,用雜湊值在計算得到在Entry[]陣列中的存放位置
-
擴容:(當超出臨界值且要新增資料的位置不為空時)預設擴容為原來的2倍,並複製資料到新陣列
2、JDK8與JDK7的不同之處
HashMap map = new HashMap()
時,底層沒有直接建立陣列,而是在首次put()時建立- 陣列是Node[]型別,而非Entry[]型別
- JDK7底層是:陣列+連結串列;而JDK8底層是:陣列+連結串列+紅黑樹
- 形成連結串列時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)
- 當陣列的某一個索引位置上的元素以連結串列形式存在的資料個數 > 8 且當前陣列的長度 > 64時,此時此索引位置上的所資料改為使用紅黑樹儲存。
3、LinkedHashMap底層實現原理
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能夠記錄新增的元素的先後順序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
六、Map介面常用方法
1、新增、刪除、修改操作:
-
Object put(Object key,Object value)
:將指定key-value新增到(或修改)當前map物件中void putAll(Map m)
:將m中的所有key-value對存放到當前map中 -
Object remove(Object key)
:移除指定key的key-value對,並返回value
void clear()
:清空當前map中的所有資料
2、 元素查詢的操作:
-
Object get(Object key)
:獲取指定key對應的value -
boolean containsKey(Object key)
:是否包含指定的keyboolean containsValue(Object value)
:是否包含指定的value -
int size()
:返回map中key-value對的個數 -
boolean isEmpty()
:判斷當前map是否為空 -
boolean equals(Object obj)
:判斷當前map和引數物件obj是否相等
3、元檢視操作的方法:
Set keySet()
:返回所有key構成的Set集合Collection values()
:返回所有value構成的Collection集合Set entrySet()
:返回所有key-value對構成的Set集合
4、總結常用方法:
- 新增:
put(Object key,Object value)
- 刪除:
remove(Object key)
- 修改:
put(Object key,Object value)
- 查詢:
get(Object key)
- 長度:
size()
- 遍歷:
keySet() / values() / entrySet()
七、Properties
1、配置檔案
-
預設存放路徑為當前工程下
-
建立配置檔案的方式
- 右鍵工程名--->new--->File:這時要寫上
.properties
,如jdbc.properties
- 右鍵工程名--->new--->Resource Bundle:這時秩序寫名稱就會自動補全字尾
- 右鍵工程名--->new--->File:這時要寫上
-
存取資料時,建議使用
setProperty(String key,String value)
方法和getProperty(String key)
方法
八、Collections工具類
1、Collections概述
- Collections是一個操作Set、List、Map等集合的工具類
- Collections 中提供了一系列靜態的方法對集合元素進行排序、查詢和修改等操作, 還提供了對集合物件設定不可變、對集合物件實現同步控制等方法
2、Collections常用方法
排序操作
- 排序操作都是針對List來講的,因為Map本身無序,何談排序
- 返回值都是void,說明對List本身做了修改
方法 | 解釋 |
---|---|
reverse(List) | 反轉List中的元素 |
shuffle(List) | 隨機排序 |
sort(List) | 自然排序 |
Sort(List, Comparator) | 定製排序 |
swap(List, int i, int j) | 交換 |
查詢、替換
方法 | 解釋 |
---|---|
Object max/min(Collection) | 返回自然排序的最大、小值 |
Object max/min(Collection, Comparator) | 返回定製排序的最大、小值 |
int frequency(Collection, Object) | 返回某集合中某元素的出現次數 |
void copy(List dest, List src) | 複製 |
boolean replaceAll(List list, Object oldVal, Object newVal) | 替換 |
- void copy(List dest, List src)中,新List容量不能小於舊List,因此要用
List dest = Arrays.asList(new Object[list.size()]);
來建立