泛型
1.泛型的概念:
泛型: 標識着 集合中存儲元素的數據類型
寫法: <數據類型(即泛型)>
// 泛型的寫法:
// 創建一個集合 ,保存a b c d
// E 泛型 Element(元素)
// 注意:前後泛型的類型要保持一致(如果後面要填的話)
// jdk1.7 菱形泛型
// 後面泛型可以不填 默認和前面的一致
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 利用迭代器遍歷
// 迭代器泛型 表示集合中保存的元素類型 [與實現類中的泛型保持一致]
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
// 如果是自己寫的類的對象,需要重寫toString()
}
2.泛型的好處
1️⃣保證了數據的安全性 (提示你 方法中要傳入的數據類型)
2️⃣避免了 向下轉型(類型強轉) [提示你 創建什麼類型的對象接受迭代器傳來的數據]
3️⃣將運行時的錯誤 轉化成了編譯錯誤 [類型接受錯誤時,不需要運行報錯,直接提示]
// 創建集合 保存3學生
// 獲取集合中的第0個學生 並打印姓名
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("東東", 16)); // 好處 1️⃣:提示你方法中傳入學生類對象
list.add(new Student("生生", 18));
list.add(new Student("柳柳", 20));
Object object = list.get(0);
Student student = (Student) object; // 好處 2️⃣:可以不用向下轉型,直接用學生類對象接受
// 好處 3️⃣:若接受對象不對,不用運行才報錯,直接報錯(一般在迭代器中比較明顯)
3.泛型的聲明
1️⃣聲明位置: 類名(接口名)<泛型>
假如:Student<S> 此時S是佔位符
注意: 泛型使用佔位符(英文字符)時 一般使用大寫字母
2️⃣什麼時候 指定泛型的真正類型?
創建對象時 會給泛型 賦值數據(對象)類型
// 舉例:泛型的聲明 與 指定數據類型
// 泛型的聲明位置 (Worker類中屬性的數據類型都爲W)
class Worker<W>{
// 利用泛型 聲明成員變量
private W w;
// 聲明set/get方法
public void setW(W w) {
this.w = w;
}
public W getW() {
return w;
}
// 成員方法
public void fun(W w) {
System.out.println(w);
System.out.println("我是fun方法");
}
// 一個類中 可不可以有多個泛型? 可以
// 需要在方法上 進行泛型的聲明
// 這個泛型 將會在 該方法被調用的時候 被傳參(不一定是該類對象的屬性)賦值
public<Y> void fun1(Y y) {
System.out.println(y);
}
// 靜態方法 能不能使用 W 泛型 ? 不能
// 當方法被調用時,泛型被賦值
public static<Q> void fun2(Q q) {
System.out.println(q);
}
}
// 指定泛型的真正數據類型
// 此時泛型佔位符W 被賦值 String
Worker<String> worker = new Worker<>();
worker.setW("wangjun");
System.out.println(worker.getW());
worker.fun("少年強則中國強");
worker.fun1('1'); // 傳入的參數是字符,該Y被賦值char
// 接口中的泛型
// 泛型接口
interface InterA<G>{
public abstract void fun(G g);
}
// 泛型類型確定 可以在 接口的實現類上確定
class InterAImpl implements InterA<String>{
@Override
public void fun(String g) {
// TODO Auto-generated method stub
}
}
多參數方法(int … num)
int ... num (輸入int類型 從0到num)
可以接受多個 int類型值 相當於參數是一個數組[0,1,...num]
使用條件:只能在方法中使用,用作傳入參數
使用限制:1.方法的參數列表中 最多隻能有一個不定長度的參數
2.不定長度的數組的位置必須是最後一個參數,不然無法編譯
調用方式兩種
1.傳入多個值 用逗號隔開
2.直接傳入數組
// 第一種方法(傳入多個值,逗號隔開)調用
注意:可以添加不同類型的參數,但是要放在前面,放在後面會報錯
(換句話說: int ... num 要放在最後面)
public static void print(String str,int ... num) {
// 遍歷(獲取你傳進來有幾個int值)
for (int i = 0; i < num.length; i++) {
System.out.println(num[i]);
}
}
// 第二種方法(輸入數組)隔開
int[] arr = {2,3,4,5};
print(" ",arr);
數組轉集合:Arrays.asList(數組名),記住用集合接收
// ❎ 錯誤的 原因:集合中只能放引用類型的數據,不能放基本數據類型
int[] array = {1,2,3,4,5};
List<int[]> list = Arrays.asList(array);
System.out.println(list);
// ✔️ 正確的 裝箱了
Integer[] array1 = {1,2,3,4,5};
List<Integer> list1 = Arrays.asList(array1);
System.out.println(list1);
// 創建一個字符串數組 保存三個名字
// 將數組 轉成集合
// 添加一個名字
String[] arr = {"東東","生生","柳柳"};
// 將數組 轉成 集合
List<String> list2 = Arrays.asList(arr);
System.out.println(list2);
// 添加名字
// UnsupportedOperationException
// 不支持操作異常
// 注意:該方法 轉完集合後 不支持對集合的長度修改
// list.add("花花");
// 該方法意義在於: 可以使用集合類中 其他方法
boolean b = list2.contains("生生");
System.out.println(b);
4.泛型在繼承中的應用: 將子類或本類放入集合中
// 前提: class Student extends Person
// 舉例:
ArrayList<Person> list = new ArrayList<Person>();
list.add(new Student()); // 添加子類
list.add(new Person()); // 添加本類
去重刪除
一般兩種方法 1.for循環刪除 2.迭代器刪除
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d"); [a,b,b,c,d] 刪除兩個b
// 1.for循環刪除
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
if (string.equals("b")) {
list.remove(i--);
// 刪除一個,其他的前移一位,若兩個連續,則只能刪除一個
// --,是爲了循環在再進行一次,刪除下一個b
}
}
System.out.println(list);
// 2.迭代器刪除
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if (str.equals("b")) {
iterator.remove();
}
}
System.out.println(list);
集合工具類:Collection.swap(list,i,j) 交換位置
Set接口-HashSet實現類
1.HashSet實現類的特點: 無序 無角標 不可重複(繼承Set接口)
自帶去重 (前提:對象地址重複,才能去重,不能根據屬性直接去重)
2.HashSet集合的建立
// 創建Set集合 添加a a b b c c
// 有序: 指的是存取的順序 存的順序就是取的順序
// 迭代器遍歷
HashSet<String> set = new HashSet<>();
set.add("f");
set.add("a");
set.add("a");
set.add("b");
set.add("b");
set.add("c");
set.add("c");
// 迭代器遍歷
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String string = (String) iterator.next();
System.out.println(string);
}
結果:
a,b,c,f(無序)
3.Set的自動去重
HashSet<Man> set = new HashSet<>();
Man m1 = new Man("1",11);
Man m2 = new Man("1",11);
Man m3 = new Man("2",22);
Man m4 = new Man("2",22);
Man m5 = new Man("3",33);
Man m6 = new Man("3",33);
set.add(m1);
set.add(m2);
set.add(m3);
set.add(m4);
set.add(m5);
set.add(m6);
Iterator<Man> iterator = set.iterator();
while (iterator.hasNext()) {
Man m = (Man) iterator.next();
System.out.println(m);
}
直接輸出結果: 一個沒刪
沒去重原因:
// 系統每創建一個對象 都會爲這個對象
// 分配一個 hashCode值
// 當向HashSet集合中 保存對象時
// 系統會先比較hashCode值是否相同
// 相同: 再調用對象的equals方法進行比較,
// 如果equals方法比較也相同,那麼就不存
// 反之,會存儲到集合中
// 不相同: 如果HashCode不相同,就相當於不是一個對象
// 那麼直接就把該對象 直接存入集合中
// 也不會調用equals方法
解決辦法:
// 重寫hashCode和equals方法
// 舉例
/*
* 隨機10個數 [10,20] 裝到集合中去重
* Integer可以直接去重
* String可以直接去重
* 系統已經重寫好了hashCode()方法和equals()方法
* 自己寫的類沒有寫好,所以自己要重寫
*/
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
int num = (int)(Math.random() * 11 + 10);
set.add(num);// 自動裝箱
}
System.out.println(set);