黑馬程式設計師—javaSE—Collection集合
一、集合框架
1. 物件陣列的概述和使用
陣列和集合儲存引用資料型別,存的都是地址值。
陣列特點:
陣列的長度固定。
一個數組的元素型別一致。
陣列既可以儲存基本資料型別,也可以儲存引用資料型別。
集合的由來及集合繼承體系圖(掌握)
集合的特點:
長度可變
集合可以儲存任意引用資料型別 (Object – 泛型)
集合只能儲存引用資料型別單列結合的體系:
Collection
|– List : 元素有序(儲存和取出的順序一致),可重複
|– ArrayList 底層資料結構:陣列
|– Vector 底層資料結構:陣列
|– LinkedList 底層資料結構:連結串列|-- Set : 元素無序(儲存和取出的順序不一致),唯一 |-- HashSet 底層資料結構:雜湊演算法 |-- TreeSet 底層資料結構:二叉樹
集合體系的學習方法:
學習頂層:因為頂層定義的是體系的共性內容。
使用底層:因為底層才是集合的具體實現。Collection集合的基本功能測試(掌握)
新增:
boolean add(Object obj)
– 是否新增成功
刪除:
boolean remove(Object obj)
– 是否刪除成功
void clear()
– 清空
判斷:
boolean isEmpty()
– 是否為空
boolean contains(Object obj)
– 是否包含指定元素長度:
int size()
– 長度轉換:
String toString()
Object[] toArray()字串長度: length()
陣列長度: length屬性
集合長度: size()集合的遍歷之集合轉陣列遍歷(理解)
注意:返回是Object陣列。也就是說元素存入集合有一個向上提升資料型別的預設操作,提升為Object型別。
那麼轉換成陣列的時候就只能是轉換成Object陣列。Object[] objs = c.toArray();
for (int x = 0; x < objs.length; x++) {
Object obj = objs[x];
}Collection集合帶All的功能測試(理解)
簡單整理下:
boolean addAll(Collection c)
將指定Collection中的所有元素都新增到呼叫方法的Collection中,返回值表示呼叫方法的Collection是否發生改變。
boolean removeAll(Collection c)
移除呼叫方法的Collection中的那些也包含在指定Collection中的所有元素(刪除交集),返回值表示呼叫方法的Collection是否發生改變。
boolean containsAll(Collection c)
判斷呼叫方法的Collection中是否包含指定Collection中所有的元素,全部包含才返回true。
boolean retainAll(Collection c)
僅保留呼叫方法的 collection 中那些也包含在指定 collection 的元素(保留交集),返回值表示呼叫方法的Collection是否發生改變。
例如:A對B做交集,A保留交集內容,B不變。返回值表示A是否發生改變。集合的遍歷之迭代器遍歷(掌握)
// 建立集合物件
// 建立元素物件並新增// 迭代器遍歷
// 通過集合物件獲取迭代器
Iterator it = c.itertor();// 判斷是否有下一個元素並獲取 while(it.hasNext()){ Obejct obj = it.next(); } 練習: 1:首先定義一個標準學生類(name,age屬性) 2:用集合儲存3個學生,然後遍歷集合。打印出每個學生的name和age。
迭代器的原理及原始碼分析
不同集合的資料結構是不一樣的,這就造成了不同的集合存取元素的方式是不一樣的。那麼直接將迭代器定義為普通實現類是不合適的。
但是不管什麼樣的集合,又都需要提供遍歷功能(也就是都要有判斷是否包含下一個元素的功能,有獲取下一個元素的功能),
那麼有相同的功能,但實現不一樣,這時把功能提取出來定義到接口裡面(規則的體現)。而在具體的集合類裡面實現這個介面即可。而又由於集合儲存資料的方式集合本身最清楚,也就是說迭代器裡面的功能如何實現是要依賴於集合的資料結構。
那麼這時就把迭代器的具體實現定義在了集合的內部,以內部類的方式實現。原則:對修改關閉,對擴充套件開放
二、List集合
1. List集合的特有功能概述和測試(掌握)
新增:
void add(int index, Object element)
– 在指定的位置新增元素
刪除:
Object remove(int index)
– 根據索引刪除,返回被刪除掉的元素
修改:
Obejct set(int index, Object element)
-- 修改指定索引處的元素,返回被替換掉的
獲取:
Object get(int index)
-- 返回指定索引處的元素
List體系的普通for迴圈遍歷
for(int x = 0; x < list.size(); x++) {
Object obj = list.get(x);
}
List集合儲存學生物件並遍歷(掌握)
我有一個集合,然後往裡邊新增hello,world,java三個元素,
我想判斷裡面有沒有“world”這個元素。
如果有,我就新增一個“javaee”元素,沒有什麼都不新增。注意:用迭代器來做。
併發修改異常產生的原因及解決方案(掌握)
ConcurrentModificationException – 併發修改異常原因: 用迭代器遍歷集合,又使用集合改變了集合的結構,這樣的修改就是併發修改,是不被允許。 解決方案: a. 用普通for遍歷集合,判斷然後改變集合的元素。元素新增到最後面。 b. 使用列表迭代器ListIterator。(用List的特有迭代器遍歷,並使用它新增元素),這時元素新增到匹配的元素後面。
開發原則:儘量不要在遍歷集合的同時去改變集合的結構。(隱患)
ListIterator
Vector的特有功能(瞭解)
Vector是執行緒安全的(同步的),效率低瞭解API的發展特點
安全 – 效率
簡化書寫資料結構之陣列和連結串列
陣列:查詢快,增刪慢
連結串列:查詢慢,增刪快陣列的長度固定的,增刪一個元素必須新建陣列
List的三個子類的特點
|– List : 元素有序(儲存和取出的順序一致),可重複
|– ArrayList
底層資料結構:陣列 – 查詢快,增刪慢
執行緒不安全的,效率高
|– Vector
底層資料結構:陣列 – 查詢快,增刪慢
執行緒安全的,效率低
|– LinkedList
底層資料結構:連結串列 – 查詢慢,增刪快
執行緒不安全的,效率高如果查詢多,選ArrayList
如果增刪多,選LinkedList– 如果什麼都不知道(或者如果都多),選ArrayList
一、ArrayList
1、去除ArrayList中重複字串元素方式
定義新集合的方式:
定義一個新的ArrayList集合;
遍歷舊集合,獲取到每一個元素,將其新增到新集合,
在新增之前做判斷,判斷新集合是否已經包含了該元素,如果不包含,才新增。
2、去除ArrayList中重複自定義物件元素
思路與上面相同。
注意事項:contains方法底層依賴元素所屬類的equals方法,所以去重寫equals方法,實現具體屬性值的比較。
補充練習:
去除ArrayList中所有的指定元素。
ArrayList al = new ArrayList();
al.add("aaa");
al.add("aaa");
al.add("aaa");
al.add("bbb");
al.add("ccc");
al.add("aaa");
al.add("aaa");
al.add("ccc");
需求:去除所有的字串“aaa”
實現方式1:
列表迭代器遍歷集合,並使用列表迭代器刪除匹配的元素
實現方式2:
用普通for迴圈正向遍歷集合,一旦刪除元素,迴圈索引--,保證元素全部能遍歷到。
實現方式3:
用普通for迴圈倒著遍歷集合,刪除匹配的元素。這樣不會有遺漏。
。。。(多種方式,歡迎思考)
二、LinkedList
1、LinkedList的特有功能
void addFirst(Object obj) – 新增到開頭
void addLast(Object obj) – 新增到結尾
Object removeFirst() -- 刪除第一個元素,並返回
Object removeLast() -- 刪除最後一個元素,並返回
Object getFirst() -- 獲取第一個元素
Object getLast() -- 獲取最後一個元素
2、棧和佇列資料結構
棧:後進先出(LIFO表)
佇列:先進先出(FIFO表)
3、用LinkedList模擬棧資料結構的集合並測試
要提供自定義的類,實現元素的新增和刪除等功能,這些功能要符合棧的結構特點。
而底層採用誰來實現,那是你自己的事了,我不關心。而這個案例恰好可以用LinkedList來實現。
注意事項:不要把自定義的類起名字為Stack。API上有。
三、泛型
1、泛型(generic)概述和基本使用
泛型把明確資料型別的操作放到建立物件或者呼叫方法的時候再明確。
JDK1.5之後的新特性。
格式:
<引用資料型別>
好處:
泛型保證集合中的資料型別一致,提高安全性。把執行期異常提前到編譯期。
那麼在JDK1.5之前怎麼實現引數的任意化呢?
是通過接收Object型別的引數實現的。因為任意類直接或間接繼承自Object。
但這樣實現的缺點就是,還是不能保證資料型別的安全一致。
Object obj1 = new Student();
Object obj2 = new Teacher();
上面兩個物件都宣告為Object型別,那獲取元素的時候到底該如何轉型呢?還是存在問題。
應用:
泛型類
泛型介面
泛型方法
2、ArrayList儲存字串和自定義物件並遍歷泛型版
3、泛型的由來
補充:泛型擦除
檢視下述兩個方法:
public void show(ArrayList<String> list) {}
public void show(ArrayList<Integer> list){}
/*
* 上述兩個方法是不會形成方法的過載的,會報方法已存在的錯誤。 原因:泛型擦除
* 泛型是1.5中引入的一個新的概念,由於不用進行強制轉換型別了,所以具有較高的安全性和易用性。
* 因為泛型其實只是在編譯器中實現的而虛擬機器並不認識泛型
* ,所以要在虛擬機器中將泛型型別進行擦除。也就是說,在編譯階段使用泛型,執行階段取消泛型,即擦除。
* 擦除是將泛型型別以其父類代替,如String變成了Object等。
* 其實在使用的時候還是進行帶強制型別的轉化,只不過這是比較安全的轉換,因為在編譯階段已經確保了資料的一致性。
*/
4、泛型類的概述及使用
明確資料型別的工作放到了建立物件的時候
5、泛型方法的概述和使用
明確資料型別的工作放到了呼叫方法的時候
方法泛型 <> 加在返回值前面。
6、泛型介面的概述和使用
一種是子類實現介面的時候明確資料型別
interface Fu<T>{}
class Zi implements Fu<String>{}
一種是子類繼續使用泛型:
class Zi<T> implements Fu<T>{}
7、泛型高階之萬用字元
<?> -- 泛型萬用字元
<? extends E> -- 向下限定,接收E及其子類型別
<? super E> -- 向上限定,接收E及其父類型別
四、集合加強
1、增強for的概述和使用(foreach)
格式:
for(陣列或者Collection集合中元素型別 變數名 :陣列或者Collection集合物件){
//直接使用變數名即可
}
注意:增強for底層是迭代器實現的,不要使用增強for的同時用集合改變結構,會報併發修改異常。
2、ArrayList儲存自定義物件並遍歷增強for版
3、三種迭代的能否刪除
a.普通for正向遍歷,可以刪除,但要注意,一旦刪除了元素,索引要減1.
for(int i = 0; i < list.size();){
if("b".equals(s)){
list.remove(i);
continue;
}
i++;
}
b.迭代器遍歷,也可刪除,但是注意要使用迭代器的刪除方法。
c.增強for遍歷時,不能刪除元素。
4、靜態匯入的概述和使用
對於靜態方法,匯入到方法的級別
格式:
import static 包名.類名.方法名;
5、可變引數的概述和使用
適用於引數型別明確但個數不確定的情況。
注意:方法裡面有多個引數,那麼可變引數必須放到最後。
6、Arrays工具類的asList()方法的使用
陣列轉換成集合;
public static <T> List<T> asList(T... a)
轉成集合後大小固定,不支援增加或者刪除操作。
引用資料型別陣列 -- 把陣列中的元素作為集合中的元素。
基本資料型別陣列 -- 把陣列物件作為一個元素新增到了集合中。
集合轉陣列:
Object[] toArray() -- 把集合轉換成Object[]
<T> T[] toArray(T[] a) -- 把集合轉換成了指定資料型別的陣列
-- 如果陣列長度小於等於集合,那麼返回陣列的長度是集合的長度
-- 如果陣列長度大於集合,那麼返回的陣列長度是指定的長度,前面儲存集合中的元素,後面儲存的是null。
7、集合巢狀之ArrayList巢狀ArrayList
// 建立學科集合物件
// 建立班級1集合物件
// 建立幾個學生物件放進班級1
// 建立班級2集合物件
// 建立幾個學生物件放進班級2
// 把班級1和2放進學科集合物件
// 遍歷
// 遍歷學科物件得到的是每一個班級集合
// 遍歷每一個班級集合得到的是每一個學生
一、HashSet集合
1、HashSet儲存字串並遍歷
Set體系無序無索引,可以使用迭代器或者增強for遍歷。
2、HashSet儲存自定義物件保證元素唯一性
HashSet儲存自定義物件保證元素唯一依賴於元素所屬類的hashCode() 和 equals() 方法。
3、HashSet儲存自定義物件保證唯一性圖解及優化
如何重寫hashCode()
自己寫的話:一般情況下是返回所有屬性的hashCode值相加(引用資料用hashCode(),基本資料直接用值)。
例如:name.hashCode() * 117 + age * 19;
4、HashSet如何保證元素唯一性的原理
整體理解:
元素存入HashSet集合時,會先走hashCode()方法,判斷元素與集合中已經存在的元素的hash值。
如果不同,元素不重複,新增。不再執行equals方法。
如果相同,再去執行equals方法,如果equals返回true,認為元素重複,不新增。
如果equals返回false,元素不重複,新增。
5、LinkedHashSet的概述和使用
LinkedHashSet 存取有序,且元素唯一。
6、產生10個1-20之間的隨機數要求隨機數不能重複
7、練習 - 使用Scanner從鍵盤讀取一行輸入,去掉其中重複字元
// 遍歷字串
char charAt(int index)
for(int x = 0; x < str.length(); x++){
char ch = str.charAt(x);
}
8、練習 - 去除ArrayList集合中的重複字串元素
使用LinkedHashSet
二、TreeSet集合
1、TreeSet儲存Integer型別的元素並遍歷
TreeSet 保證元素唯一,並且可以實現排序。 – 自然順序
2、TreeSet儲存自定義物件
TreeSet儲存自定義物件實現排序的方式1:
自然排序 -- 讓元素具備比較性:
元素所屬類實現Comparable介面,重寫compareTo()方法。
根據compareTo()返回值:
返回0,元素重複,不新增。
返回正數,元素往後放。
返回負數,元素往前放。
元素取出的時候是按照左中右的原則取出的。
3、TreeSet保證元素唯一和自然排序的原理和圖解
底層原始碼是建立在二叉樹的基礎上,但不是最簡單的二叉樹。自平衡二叉樹。
4、TreeSet儲存自定義物件並遍歷練習1 -- 按姓名排序
5、TreeSet儲存自定義物件並遍歷練習2 -- 按照姓名的長度排序
6、TreeSet保證元素唯一和比較器排序的原理
TreeSet儲存自定義物件實現排序的方式2:
比較器排序 -- 讓集合具備比較性:
定義個一個類實現Comparator,重寫compare() 方法
使用的是TreeSet的帶引數(帶比較器引數的)構造
7、TreeSet原理 - 兩種排序方式總結
8、練習 - 對ArrayList集合中的元素排序且不能去除重複元素
9、練習 - 鍵盤錄入字串,實現對字元進行排序
10、練習 - 鍵盤錄入整數,實現倒序列印
注意 :鍵盤錄入要自定義結束標記
11、練習 - 鍵盤錄入學生資訊,按照總分排序後輸出在控制檯