天天看點

Java集合類基礎(List,Set,Map)集合類簡介周遊集合的方法:Iterator接口和foreach循環List接口Set接口Map接口結語

集合類簡介

Java的集合類主要由兩個接口派生而出:Collection和Map,這兩個接口又包含了一些接口或實作類。

一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供Collection的直接實作,提供的類都是繼承自Collection的“子接口”如List和Set。是以Set、List和Map可以看做集合的三大部分。

Collections是一個集合架構的幫助類,裡面包含一些對集合的排序,搜尋以及序列化的操作。

集合架構的優點如下:

(1)使用核心集合類降低開發成本,而非實作我們自己的集合類。

(2)随着使用經過嚴格測試的集合架構類,代碼品質會得到提高。

(3)通過使用JDK附帶的集合類,可以降低代碼維護成本。

(4)複用性和可操作性。

使用泛型的好處?

(1)泛型允許我們為集合提供一個可以容納的對象類型,是以,如果你添加其它類型的任何元素,它會在編譯時報錯。這避免了在運作時出現ClassCastException,因為你将會在編譯時得到報錯資訊。

(2)泛型也使得代碼整潔,我們不需要使用顯式轉換和instanceOf操作符。

(3)它也給運作時帶來好處,因為不會産生類型檢查的位元組碼指令。

周遊集合的方法:Iterator接口和foreach循環

Collection有一個重要的方法:iterator(),傳回一個疊代器Iterator,用于周遊集合的所有元素。Iterator模式可以把通路邏輯從不同的集合類中抽象出來,進而避免向用戶端暴露集合的内部結構。

每一種集合類傳回的Iterator具體類型可能不同,但它們都實作了Iterator接口,是以,我們不需要關心到底是哪種Iterator,它隻需要獲得這個Iterator接口即可,這就是接口的好處,面向對象的威力。

疊代器取代了Java集合架構中的Enumeration(遺留類),Iterator更加安全,因為當一個集合正在被周遊的時候,它會阻止其它線程去修改集合(以抛異常的方式)。要確定周遊過程順利完成,必須保證周遊過程中不更改集合的内容(Iterator的remove()方法除外),是以,確定周遊可靠的原則是:隻在一個線程中使用這個集合,或者在多線程中對周遊代碼進行同步。

Iterator接口提供的三種方法:

(1)boolean hasNext():傳回集合裡的下一個元素。

(2)Object next():傳回集合裡下一個元素。

(3)void remove();删除集合裡上一次next方法傳回的元素。

典型的用法如下:

public class TestIterator { 
    public static void main(String[] args) { 
        //建立一個集合 
        Collection books = new HashSet(); 
        books.add("輕量級J2EE企業應用實戰"); 
        books.add("Struts2權威指南"); 
        books.add("基于J2EE的Ajax寶典"); 
        //擷取books集合對應的疊代器 
        Iterator<String> it = books.iterator(); 
        while(it.hasNext()) { 
            String book = it.next(); 
            System.out.println(book); 
            if (book.equals("Struts2權威指南")) { 
                it.remove();
                //使用Iterator疊代過程中,不可修改集合元素,下面代碼引發異常
                //books.remove(book); 
            } 
            //對book變量指派,不會改變集合元素本身 
            book = "測試字元串"; 
        } 
        System.out.println(books); 
    } 
}
           

程式運作結果:

Struts2權威指南 
基于J2EE的Ajax寶典 
輕量級J2EE企業應用實戰 
[基于J2EE的Ajax寶典, 輕量級J2EE企業應用實戰]
           

說明:

(1)通過語句

book = "測試字元串";

對疊代變量book進行指派時,當我們再次輸出books集合時,集合裡的元素沒有任何變化。即當使用Iterator對集合元素進行疊代時,Iterator并不是把集合元素本身傳給疊代變量,而是把集合元素的值傳給了疊代變量。因為java都是值傳遞,詳見:java參數傳遞的方式。

(2)當使用Iterator來通路Collection集合元素時,隻有通過Iterator的remove方法才能删除目前元素。其它在周遊過程中更改集合内容的行為都會引發java.util.ConcurrentModificationExcption異常。

對比一下,

for each

的方式線程不安全,但簡化了代碼,也不用管是否越界。

List接口

List是有序的Collection,可以有相同的元素,使用此接口能夠精确的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似于數組下标)來通路List中的元素,類似于Java的數組。

除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,傳回一個ListIterator接口,和标準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,删除,設定元素,還能向前或向後周遊previous()。

實作List接口的常用類有LinkedList,ArrayList,Vector和Stack(後兩個是遺留類)。

LinkedList類

LinkedList實作了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。擅長頻繁增删元素。

注意LinkedList沒有同步方法。如果多個線程同時通路一個List,則必須自己實作通路同步。一種解決方法是在建立List時構造一個同步的List:

List list = Collections.synchronizedList(new LinkedList(…));
           

ArrayList類

ArrayList實作了可變大小的數組,允許null元素,沒有同步方法。擅長随機通路元素。與Array不同的是,Array可以容納基本類型和對象,而ArrayList隻能容納對象。

每個ArrayList執行個體都有一個容量(Capacity),即用于存儲元素的數組的大小。這個容量可随着不斷添加新元素而自動增加,但是增長算法并沒有定義。當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。

Vector類和Stack 類

作為遺留類不推薦使用。Vector非常類似ArrayList,兩者都是基于索引的,内部由一個數組支援,都維護插入順序。但是Vector是同步的。

Stack繼承自Vector,提供5個額外的方法:基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。

Set接口

Set是一種不包含重複的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。

請注意:必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀态導緻Object.equals(Object)=true将導緻一些問題。

Set接口有以下幾種實作:

(1)HashSet : 為快速查找設計的Set,主要的特點是:不能存放重複元素,而且采用散列的存儲方法,是以沒有順序。這裡所說的沒有順序是指元素插入的順序與輸出的順序不一緻。

(2)TreeSet : 儲存次序的Set, 底層為樹結構。使用它可以從Set中提取有序的序列。

(3)LinkedHashSet : 具有HashSet的查詢速度,且内部使用連結清單維護元素的順序(插入的次序)。于是在使用疊代器周遊Set時,結果會按元素插入的次序顯示。

Map接口

常用有HashMap、LinkedHashMap、HashTable和TreeMap。詳情請見:

Java8的HashMap詳解(存儲結構,功能實作,擴容優化,線程安全,周遊方法)。

這裡主要講一下,由于作為key的對象将通過計算其散列函數來确定與之對應的value的位置,是以任何作為key的對象都必須實作hashCode和equals方法。

hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同,如果兩個不同對象的hashCode相同,這種現象稱為沖突,沖突會導緻操作哈希表的時間開銷增大,是以盡量定義好的hashCode()方法,能加快哈希表的操作。

如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法傳回null),要避免這種問題,隻需要牢記一條:要同時複寫equals方法和hashCode方法,而不要隻寫其中一個。其實最好使用JDK提供的不可變類作為Map的key,可以避免自己實作hashCode()和equals()。

結語

以上就是集合架構相關的接口和類的介紹,最後要注意的是,盡量傳回接口而非實際的類型,如傳回List而非ArrayList,這樣如果以後需要将ArrayList換成LinkedList時,用戶端代碼不用改變。這就是針對抽象程式設計。

參考自:大公司最喜歡問的Java集合類面試題

Java集合架構總結—超詳細-适合面試

40個Java集合面試問題和答案