List
陣列 (Array) 是一個長度固定的資料結構,如果要儲存的資料數量無法事先預測有多少,陣列就派不上用場。此時就需要一個能隨時變換長度的資料結構,List 就是依此需求而產生。
List 是一個抽象類別,規範存放資料的方法,存放的資料都是物件型態,如果放入的資料是八大原生資料,會自動轉成 Integer、Float 等物件型態。也就是說 List 什麼資料都可以存放,是一個很髒的百寶袋。
因為 List 是抽象類別,不可以直接產生物件。而 ArrayList 則是實作 List 的類別,所以需使用 ArrayList 存放資料。不過 ArrayList 並不是雙向連結,每當新增刪除資料時,需進行大量的搬移,所以效能非常差。
package net.ddns.mahaljsp; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { G.encoding(); List list=new ArrayList(); list.add("Sunday"); list.add("Monday"); list.add("Tuesday"); list.add("Wednesday"); list.add("Thursday"); list.add("Friday"); list.add("Saturday"); list.add(10); for (Object o:list){ System.out.println(o.toString()); } } }
上述代碼什麼資料都可以存放,最後取出時是使用 Object 型態。
泛型(Generics)
泛型又稱廣泛的類型,透過型別的參數化來達成。使用泛型可以減少重複撰寫程式碼,用如下例子說明。
class Pikachu{} class Eve{} //末使用泛型 class PikachuGroup{ private Pikachu[] array; public Pikachu[] getArray(){ return array; } } class EveGroup{ private Eve[] array; public Eve[] getArray(){ return array; } } //上述二個xxxGroup, 可以改寫成如下一個 //使用泛型 class Group<T>{ private T[] array; public T[] getArray(){ return array; } }
而在 main 裏可以寫成如下
public class JavaApplication1 { public static void main(String[] args) { PikachuGroup pg1 = new PikachuGroup(); Group<Pikachu> pg2 = new Group<>(); Pikachu[] array1 = pg1.getArray();//返回 Pikachu[] 的型態 Pikachu[] array2 = pg2.getArray();//同樣是返回 Pikachu[] 的型態 Group<Eve> eg3 = new Group<>(); Eve[] array3 = eg3.getArray();//此時返回Eve[]的型態 } }
pg2 及 eg3 同樣是 Group 的型態,但 pg2.getArray() 及 eg3.getArray() 卻傳回不同的型態。
泛化(Generalization)
List 可以存放任何型態的資料,非常雜亂。底下代碼藍色部份會導致執行後發生 RuntimeError 而閃退。
public class GenericsTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add("Thomas");
list.add("John");
list.add("Merry");
list.add(new Integer(10));//不會錯
for (Object o:list){
String d=(String)o;//轉到Integer時發生RuntimeError
System.out.println(d);
}
}
}
如果能強制規定 List 只能存放一種資料型別,就不會發生 RuntimeError 的閃退問題。規定只能存放特定型別的功能,稱為泛化 (Generalization)。下面的代碼指定只能存入 String。
public class GenericsTest { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("Thomas"); list.add("John"); list.add("Merry"); list.add(new Integer(100));//Compile error for (String o:list){ System.out.println(o); } } }
泛化並不是在 Runtime 時期處理,而是在編譯時期就開始檢查,所以上述紅色那行可以很輕易的在編譯期就找出錯誤。
泛化一詞是指在編譯時期就產生一個專屬的類別,產生此專屬類別這個動作稱為泛化。
Vector 類別
Vector 是 C++ 語言唯一的 List 類別。早期的 Java 模仿 C++ 也使用 Vector,但後來被放棄了。如果使用如下
Vector v=new Vector();
編譯器會提出警告–Obsolete collection。阿諾的名言~~I am old, but not obsolete.
參數化
泛型變數 <T> 代表任何型態. 任何變數名稱都可以使用, 不是一定要用<T>, 但通常有不成文的規定如下
T : type
E: element
K: Key
V:Value
S, U第二第三型態
Diamond <>
因為 OCP 把 <> 稱為 Diamond(鑽石),所以這個名詞要記一下。
泛化精簡
早期 Java 6.0 及之前的版本,規定一定要寫成如下
List<String> list=new ArrayList<String>();
這種寫法左邊要 String,右邊也要 String,是種蠻煩人的設計。所以到了 Java 7.0 之後,改良為如下。
List<String> list=new ArrayList<>();
泛型與原生資料
泛型不能接收原生基本資料,所以只能將原生資料包裝成類別。
List<Integer> list=new ArrayList<>();
Iterator 與泛型
Iterator 本身也支援泛型
package net.ddns.mahaljsp; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Test { public static void main(String[] args) { G.encoding(); List<String> list=new ArrayList<>(); list.add("Sunday"); list.add("Monday"); list.add("Tuesday"); list.add("Wednesday"); list.add("Thursday"); list.add("Friday"); list.add("Saturday"); Iterator<String> it=list.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }
方法回傳值的泛化
如下例子, 方法的傳回值也是泛化的
public static List<String> getList(){
List list=new ArrayList<>();
list.add("Thomas");
list.add("John");
list.add("Merry");
return list;
}
泛型多型
泛型多型的轉換,只允許基底型別的轉換,參數型別必需是一模一樣的。
Vector<Integer> v1=new Vector<>();
List<Integer> v2=v1; //ok
Vector<Integer> v1=new Vector<>();
Vector<Number>v2=v1; //編譯錯誤
泛型萬用型別<?>
方法會接受來自不同的呼叫者,而每個呼叫者的型態都不一樣時,此時就可以使用 <?>。
public class GenericsTest { public static void main(String[] args) { List<String> list1=new ArrayList<>(); list1.add("Thomas"); list1.add("John"); list1.add("Merry"); List<Integer> list2=new ArrayList<>(); list2.add(5); list2.add(10); list2.add(15); printList(list1); printList(list2); } public static void printList(List<?> list){ Iterator<?> it=list.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }
上述其實不需要 <?>,可以改成如下
public static void printList(List list){
............
}
限制參數的型別
如果寫成如下,就只有 Integer,Float,Double 等數字才可傳入。
public static void printList(List<? extends Number>list){
.....
}