今日內容
- 集合的概述和Collection集合
- List集合
- 數據結構和ArrayList集合的相關案例
01. 爲什麼會出現集合類
- 1. 集合的特點
- 2. 爲什麼要有集合
總結:
1. 相同點: 集合和數組都是容器, 用來存儲數據的
不同點:
數組存儲:可以存儲基本數據類型, 也可以存儲引用數據類型
存儲基本數據類型的時候, 存儲的是值 int[] arr = {11,22,33};
Person p1 = new Perosn(); Person p2 = new Person();
存儲引用數據類型的時候, 存儲的是地址值 Person[] pArr = new Person[2];
pArr[0] = p1; pArr[1] = p2;
集合存儲: 只能存儲引用數據類型
長度: 數組的長度是固定的, 集合的長度可變的.
2. 舉例:
學生管理系統, 記錄學生的各項信息
如果使用數組進行存儲, 數組長度初始爲80個大小, 這時候插班進來的第81個學生就沒有位置存儲了.
問題產生: 使用數組就不合適了
解決方案: 使用一個長度可變的容器, [集合]
02. 集合類體系結構圖
- 再畫一張
03. 創建Collection集合對象並添加元素
- 爲什麼要先學習Collection?
- 創建集合對象的時候, <>代表什麼?
總結:
1. Collection是單列集合的根接口, 內部定義的規則, 所有實現類都會具備一份
/*
* 細節1: 如果一個類沒有找到我們使用的方法, 可以去父類中查詢
*
* 問題: 接口到底有沒有繼承Object類
*
* 細節2: 接口沒有繼承Object類, 但是接口底層會有Object類所有方法的引用
*
* 目的: 就是爲了方式多態調用方法的編譯報錯!
*/
2. <> : 泛型(jdk1.5新特性) -> 用於限制集合中存儲的數據類型
集合存儲: 只能存儲引用數據類型, 是因爲泛型的特點, <>中只能編寫引用數據類型
代碼:
public class Demo1_Collection {
public static void main(String[] args) {
// 1. 創建集合對象
// 在jdk1.7版本之後, 右側的尖括號中可以不寫類型, 菱形泛型
// <> : 泛型 -> 用於限制集合中存儲的數據類型
Collection<String> c = new ArrayList<>();
// 2. 向集合中添加元素
c.add("abc");
c.add("bbb");
// 3. 展示集合中的元素
System.out.println(c.toString());
}
}
04. Collection集合的成員方法
- boolean add(E e):添加元素
- boolean remove(Object o):從集合中移除元素 ***
- void clear():清空集合中的元素
- boolean contains(Object o):判斷集合中是否存在指定的元素 ***
- boolean isEmpty():判斷集合是否爲空
-
int size():集合的長度,也就是集合中元素的個數
示例代碼:
public static void main(String[] args) { // 1. 創建集合對象, 容器中存儲的是Person對象 Collection<Person> c = new ArrayList<>(); // 2. 向集合中添加Person對象. c.add(new Person("張三", 23)); c.add(new Person("李四", 24)); c.add(new Person("王五", 25)); // 注意: 如果添加的元素是自定義對象的話, 那麼沒有重寫toString, 將打印地址值 System.out.println(c); // 3. 調用集合的刪除方法, 刪除張三 // 注意 : remove方法底層依賴於equals方法. c.remove(new Person("張三", 23)); System.out.println(c); // 4. 調用集合中判斷是否包含的方法 // 注意 : contains方法底層依賴於equals方法. System.out.println(c.contains(new Person("李四", 24))); // 5. 調用集合的清空方法 c.clear(); // 6. 調用集合判斷是否爲空的方法 System.out.println(c.isEmpty()); // 7. 打印集合的長度 System.out.println(c.size()); }
05. Collection集合的遍歷
- 使用迭代器遍歷集合的步驟
總結:
1. 調用集合的iterator方法, 獲取迭代器
Iterator<String> it = c.iterator();
2. 使用循環判斷集合中是否還有元素
while(it.hasNext()){
}
3. 使用next方法元素取出
while(it.hasNext()){
String s = it.next();
}
示例代碼:
public static void main(String[] args) {
Collection<Person> c = new ArrayList<>();
c.add(new Person("張三", 23));
c.add(new Person("李四", 24));
c.add(new Person("王五", 25));
// 1. 獲取迭代器對象
Iterator<Person> it = c.iterator();
// 2. 循環判斷集合中是否還有元素
while(it.hasNext()){
// 3. 獲取元素
Person p = it.next();
System.out.println(p);
}
}
06. 集合(迭代器)使用步驟圖解
- 看圖說話
07. Collection集合的練習存儲自定義對象並遍歷
需求: 定義一個標準的學生類, 創建三個學生對象存儲到集合容器中, 並遍歷集合.
6分鐘練習
08. List集合的特點
- List集合有什麼特點?
總結:
1. 存取有序
2. 有索引
3. 可以存儲重複的
09. List集合的特有成員方法
- void add(int index, E element) :將元素添加到index索引位置上
- E get(int index) :根據index索引獲取元素
- E remove(int index) :根據index索引刪除元素
- E set(int index, E element):將index索引位置的的元素設置爲element
10. List集合的普通for循環遍歷
- 案例演示
public class Test2_List {
/*
* List集合特有的一種遍歷方式
* 1. get(int index) : 根據傳入的索引, 獲取指定位置的元素
* 2. size() : 返回集合的長度
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
// String s1 = list.get(0);
// String s2 = list.get(1);
// String s3 = list.get(2);
// String s4 = list.get(3);
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
}
11. List集合的練習存儲自定義對象並遍歷
- 需求: 定義一個標準的學生類, 創建三個學生對象存儲到集合容器中, 並遍歷集合.
1. 普通for循環
2. 普通迭代器
3. ListIterator特有的迭代器 --> 基本使用和普通迭代器一致
// 1. 獲取列表迭代器
ListIterator<Student> listIt = list.listIterator();
// 2. 判斷集合中是否還有元素
while(listIt.hasNext()){
// 3. 調用next獲取元素
System.out.println(listIt.next());
}
12. 列表迭代器的特有功能(瞭解)
- 方法摘要:
總結:
1.boolean hasPrevious() : 判斷集合是否還有上一個元素
2.E previous() : 獲取集合中的上一個元素.
注意: 特有的功能比較雞肋
倒序遍歷之前, 必須有正序遍歷的鋪墊, 才能取出來元素
List<Person> list = new ArrayList<>();
list.add(new Person("張三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
// 1. 獲取列表迭代器
ListIterator<Person> it = list.listIterator();
// 2. 循環判斷是否還有元素
while(it.hasNext()){
// 3. 使用next方法獲取元素
System.out.println(it.next());
}
System.out.println("----------------------");
while(it.hasPrevious()){
System.out.println(it.previous());
}
- 5分鐘時間練習
13. 併發修改異常產生的原因及解決方案(重點)
- 什麼情況下會產生併發修改異常?
- 如何解決?
總結:
1. 當使用迭代器在遍歷集合的過程中, 使用集合的添加或刪除方法操作元素
就會產生併發修改異常
2. 不讓使用集合的添加或刪除, 就使用迭代器自身的添加或刪除
刪除: 普通迭代器中存在
添加: 列表迭代器
擴展:
如果用集合的remove方法刪除的是倒數第二個元素的時候, 則不會報出併發修改異常(瞭解)
原因: 略過了編譯器的檢查. -> 少調用了一次checkxxxxx方法
14. 增強for(jdk1.5的新特性)的概述和使用
- 增強for循環的格式是?
- 增強for循環底層依賴於什麼技術實現?
總結:
1.
for(數據類型 變量名 : 要遍歷的數組或集合){
}
2.
增強for循環底層是迭代器實現的
只要是能用迭代器遍歷的容器, 都可以使用增強for循環改進.
注意1: 增強for循環不能使用集合的刪除方法, 會產生併發修改異常
注意2: 增強for循環中, 不能刪除元素
15. 增強for的練習List集合存儲自定義對象並遍歷
- 案例演示
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("張三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
for(Person p : list){
System.out.println(p);
}
}
16. 常見數據結構之棧和隊列
- 棧內存結構什麼特點?
- 隊列內存結構什麼特點?
總結:
1: 桟內存結構,類似於手槍彈夾
特點: 先進後出
2: 隊列內存結構, 類似於一條管道
特點: 先進先出
17. 常見數據結構之數組和鏈表
- 數組
優點:查詢快, 修改快
原因: 因爲數組有索引, 有索引的話, 就可以快速的定位到要操作的元素, 並快速的進行修改.
缺點:增刪慢
原因: 因爲每一次的添加或刪除都需要大批量的操作數據中的元素
刪除: 將數組中的元素刪除之後, 後面的元素會整體的向前移動一位
假如數組長度爲100, 我們將第一個元素進行刪除, 後續的99個元素都要進行移動
增加: 以ArrayList爲舉例, 底層就是數組結構的, 假設底層數組長度爲10個大小, 我們在添加第11個元素的時候
底層就會根據原數組, 自動擴容一個1.5倍大小的新數組, 再將數組內容拷貝一份到新數組中去, 隨後再把第11個元素進行添加.
- 鏈表
優點: 增刪快
只需要將兩個元素的引用切斷, 然後重新規劃引用即可, 不會影響到過多的元素.
刪除也是如此.
缺點: 查詢慢, 修改慢
每一次查找元素, 都會在鏈表結構當中, 逐個的做元素檢索.
問題: 查詢元素的時候從頭開始查, 還是從尾開始查?
底層源碼會進行一個判斷, 如果離頭近, 就從前往後找, 如果離尾近, 從後往前找.
問題: 如何進行的判斷?
會根據索引進行折半查找
問題: LinkedList有索引嗎?
因爲是List派系下的子類,所以存在索引, 但是查找的時候就是不用!
18. List集合子類特點及ArrayList集合存儲字符串並遍歷
- ArrayList集合的特點, 完成什麼樣的功能較爲合適?
總結:
1.
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("111");
list.add("222");
list.add("333");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("-------------");
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("--------------------");
for(String s : list){
System.out.println(s);
}
19. ArrayList集合的練習存儲自定義對象並遍歷
- 回顧3種集合的迭代方式
三種遍歷是否能刪除元素.
1. 迭代器: 可以刪除, 但是在調用remove方法之前必須調用next方法
問題: next方法會將指針向後移動一位, 爲什麼不需要--
答案: 底層自動幫我們實現了--的操作.
2. 普通for : 可以刪除, 但是索引需要--
3. 增強for : 不能刪, 因爲會產生併發修改異常
- 案例演示LinkedList集合的使用.
public static void main(String[] args) {
// 底層是鏈表結構
LinkedList<String> list = new LinkedList<>();
// 調用從Collection中繼承下來的add方法添加元素
list.add("1");
list.add("2");
list.add("3");
// LinkedList特有的方法: 在頭部添加元素
list.addFirst("4");
list.addFirst("5");
// LinkedList特有的方法: 在尾部添加元素
list.addLast("6");
// LinkedList特有的方法: 刪除第一個元素
list.removeFirst();
// LinkedList特有的方法: 刪除最後一個元素
list.removeLast();
System.out.println(list);
}
- 問題: LinkedList集合底層是鏈表結構, 那麼查詢元素的時候從前開始找, 還是從後開始找?
總結:
1. 會查看要查找的元素, 離頭近還是離尾近, 如果離頭近, 就從頭開始找, 反之, 就從尾開始找.
2. LinkedList雖然是鏈表結構, 但是底層也有索引 !