標籤:lis pre oid 因此 擴充 而不是 強制 視圖 推理
一、集合(Collections)
Java使用集合來組織和管理對象。
1、Java的集合類
集合類主要負責儲存、盛裝和管理對象,因此集合類也被稱為容器類。
集合類分為Set、List、Map和Queue四大體系。
- Set 代表無序、不可重複集合;
- List 代表有序、可重複集合;
- Map 代表具有映射關係元素的集合;
- Queue 代表隊列,實現元素的先進先出管理。
數組也是一種集合類,它是能隨機儲存和訪問引用序列的諸多方法中最高效的一種,當追求高效的資料訪問時,數組是很不錯的選擇。
2、集合與泛型
所有集合類都位於java.util包中,集合中只能儲存對象的引用。集合類把它所含有的元素看成是Object的執行個體,這樣方便但是也有隱患,即多個類型不同的元素被放入一個集合中,會增加集合訪問時類型轉換的困難,甚至會產生錯誤。
泛型的引入改善了這種情況,使用泛型來限制集合裡元素的類型,並讓集合記住元素的類型。這樣可以允許編譯器檢查加入集合的元素類型,避免實值型別不一致的錯誤。
3、Java集合架構介紹
集合架構是一個用來表示和操作集合的統一架構,包含實現集合的介面和類。
Java的整個集合架構圍繞一組標準介面設計,開發人員可以直接使用這些介面的標準實現,也可以使用集合架構介面實現自訂的集合實作類別。
集合架構的設計應滿足一下三個目標:
- 高效能。保證演算法的實現效率。
- 互通性。
- 高擴充性。對集合進行擴充是簡單的,只需要實現特定介面即可。
4、Java集合架構基於統一的方式組織和管理對象,包含3個方面:
- 介面。介面定義了集合操作的行為規約,它形成集合的高層視圖。
- 實作類別。是集合介面的具體表現。本質上說,是可重複使用的資料結構。
- 演算法。實現集合操作中常用的演算法,比如搜尋和排序。這些演算法通過多態實現,即相同的方法在相似的介面上有著不同的實現
5、集合介面
集合架構定義了一組介面,介面申明了對特定類型的集合可以執行的操作。
Java的集合類主要由兩個介面派生而出——Collection和Map,它們是Java集合架構的根介面。
6、集合類
標準集合類實現了Collection介面,其中一些是抽象類別,實現部分介面,其它是具體類,可以直接在代碼中使用。
所有實現Collection介面的類都必須提供兩個標準的建構函式:
- 無參建構函式——用於建立一個空的Collection;
- 使用Collection作為參數的建構函式——用於建立一個新的Collection,目的是複製。
二、泛型(Generics)
泛型允許在定義類、介面和方法時使類型(類、介面)成為參數,聲明的型別參數在使用時用具體的類型替換。
泛型主要應用在集合架構中。
1、為什麼使用泛型
- 提高程式的型別安全;泛型的使用讓編譯器可以驗證類型假設。
- 有助於避免(強制)轉型,使得編譯器能夠在編譯時間發現轉型錯誤而不用等到運行時
- 可以實現通用演算法
2、泛型類
泛型類的定義和聲明:
類名之後通過<>指定一個或者多個型別參數的名字,同時還可以對型別參數的取值範圍進行限定,多個型別參數之間使用逗號分隔。
public class Matrix<T>{ ….}
泛型的使用:
定義完型別參數後,可以在該類幾乎任意地方(靜態塊、靜態屬性、靜態方法除外)使用型別參數。注意,父類定義的型別參數不能被子類繼承。
/*執行個體化泛型類*/Matrix<Float> ft=new Matrix<Float>();
3、泛型方法
方法也可以泛型化,不管定義他們的類是不是泛型化。
為什麼要使用泛型方法而不是將類型T添加到類定義中呢?
- 當泛型方法是靜態時,不能使用類的型別參數。
- 當T上的類型約束對於方法是局部時,這意味著沒有在類的另一個方法簽名中使用相同類型的T的約束。泛型方法的型別參數是局部的,可以簡化封閉類型的簽名。
聲明泛型方法:
public <T> T ifThenElse(boolean b, T first, T second) { return b? first : second;}
泛型類是在執行個體化類時指明泛型的具體類型,泛型方法是在調用方法時指明泛型的具體類型。
編譯器會允許調用下面的代碼時使用型別推斷來推斷出T的類型,並用實際參數替代T:
String s=ifThenElse(b, ”a”, ”b”);Integer i=ifThenElse(b, new Integer(1), new Integer(2));
4、類型限制
有時我們需要限制型別參數的範圍。
類型萬用字元(?),Matrix<?>表示任意的泛型型別。
/*表示add()可以接受任意泛型型別的參數。*/ public void add(Matrix<?> m);
這種情況下,add()可以接受的參數類型太寬泛了。開發人員可能希望限制參數的具體類型,例如只希望接受Number及其子類的類型的變數,而不接受Random、Locale等類型的變數。這樣就要對萬用字元有所限制。
如Matrix類,使用了型別參數T,我們讓Number類作為類型上界來限制這個型別參數:
/*表示Matrix中包含的參數類型是Number及其子類*/public class Matrix<T extends Numbers>{…}
當引入了類型上界後,在使用類型時就可以使用類型上界類(Number)中定義的方法。
同理我們可以使用Number類作為類型下界:
/*表示Matrix中包含的參數類型是Number及其父類*/public class Matrix<? super Number>{…}
5、類型擦除(Type Erasure)
泛型是在編譯器這個層次實現的,在產生的位元組代碼中是不包含泛型的類型資訊的。
使用泛型時加上的型別參數,會被編譯器在編譯時間去掉,這個過程稱為類型擦除。也就是說,由泛型附加的類型資訊對JVM來說都是不可見的。
編譯器在編譯時間儘可能地發現可能出錯的地方,但是仍然無法避免在運行時刻出現類型轉換異常的情況。
類型擦除的過程:
首先,找到用來替換型別參數的具體類。如果指定了型別參數的上界的話,則使用上界。
然後,把代碼中的型別參數都替換成具體類型,同時去掉出現的型別宣告,即<>的內容;
理解了類型擦除,就會明白編譯器承擔了全部的類型檢查工作。編譯器禁止某些泛型的使用方式,是為了確保類型的安全性。
很多泛型的特性都與類型擦除有關,如
- 靜態變數是被泛型類的所有執行個體共用的,即泛型不能用於靜態變數
- 泛型的型別參數不能用在異常處理的catch語句中,因為異常處理是由JVM在運行時刻進行的。由於類型擦除,JVM已經無法區分源於同一泛型型別的兩個不同類型的異常。
6、開發泛型類
因為類型擦除機制,型別參數並不能用來(在類中)建立對象(比如T t=new T();)或是靜態變數的類型。
/*泛型類*/public class Lhist<V>{}/*建立泛型類執行個體*/Lhist<Integer>li=new Lhist<Integer>(30);
7、泛型的最佳實務
使用泛型常見的實踐原則:
- 在代碼中避免泛型類和原始類型混用,如List<String>和List不應共同使用。
- 使用帶萬用字元(?)的泛型類時,需要明確萬用字元所代表的一群組類型的概念。
- 不能執行個體化泛型型別變數,然後利用反射的newInstance來建立執行個體;同樣,不能建立一個泛型的數組。
- 盡量不要忽視編譯器給出的警告。
Java 集合和泛型