1. 程式人生 > >Java 集合和泛型

Java 集合和泛型

ron 接口 關系 ons 增加 pre pos 接口設計 操作

  一、集合(Collections)

  Java使用集合來組織和管理對象。

  1、Java的集合類

  集合類主要負責保存、盛裝和管理對象,因此集合類也被稱為容器類。

  集合類分為Set、List、Map和Queue四大體系。

  • Set 代表無序、不可重復集合;
  • List 代表有序、可重復集合;
  • Map 代表具有映射關系元素的集合;
  • Queue 代表隊列,實現元素的先進先出管理。

  數組也是一種集合類,它是能隨機存儲和訪問引用序列的諸多方法中最高效的一種,當追求高效的數據訪問時,數組是很不錯的選擇。

  2、集合與泛型

  所有集合類都位於java.util包中,集合中只能保存對象的引用。集合類把它所含有的元素看成是Object的實例,這樣方便但是也有隱患,即多個類型不同的元素被放入一個集合中,會增加集合訪問時類型轉換的困難,甚至會產生錯誤。

  泛型的引入改善了這種情況,使用泛型來限制集合裏元素的類型,並讓集合記住元素的類型。這樣可以允許編譯器檢查加入集合的元素類型,避免值類型不一致的錯誤。

  3、Java集合框架介紹

  集合框架是一個用來表示和操作集合的統一架構,包含實現集合的接口和類。

  Java的整個集合框架圍繞一組標準接口設計,開發者可以直接使用這些接口的標準實現,也可以使用集合框架接口實現自定義的集合實現類。

  集合框架的設計應滿足一下三個目標:

  • 高性能。保證算法的實現效率。
  • 互操作性。
  • 高擴展性。對集合進行擴展是簡單的,只需要實現特定接口即可。

  

  4、Java集合框架基於統一的方式組織和管理對象,包含3個方面:

  • 接口。接口定義了集合操作的行為規約,它形成集合的高層視圖。
  • 實現類。是集合接口的具體表現。本質上說,是可重復使用的數據結構。
  • 算法。實現集合操作中常用的算法,比如搜索和排序。這些算法通過多態實現,即相同的方法在相似的接口上有著不同的實現

  5、集合接口

  集合框架定義了一組接口,接口申明了對特定類型的集合可以執行的操作。

  Java的集合類主要由兩個接口派生而出——CollectionMap,它們是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 集合和泛型