java集合架構

 綜述

    結合框架體系應該最重要的是如何靈活利用三種接口,set,map,list,他們如何遍歷,各有什麼特徵,怎麼樣去處理,這是關鍵,在這個基礎上再去掌握在什麼場合用什麼類型的接口。比如說list和set,set是無序的一個空袋子,當我們只需要放入取出,這個接口當然是最實用的,但是如果我們需要按序取出,這個方法就不能用了,而要用到list,map接口就有個特點,它有一個特定的key值,而一個key值又對應着一個value,這個value值中就可以存入很多的東西了,比如姓名,出生年月,性別等,而且可以指定key取出對應的value!(自寫)

    如果涉及到堆棧,隊列等操作,應該考慮用List,對於需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機訪問元素,應該使用ArrayList。
如果程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,如果多個線程可能同時操作一個類,應該使用同步的類。
要特別注意對哈希表的操作,作爲key的對象要正確複寫equals和hashCode方法。
儘量返回接口而非實際的類型,如返回List而非ArrayList,這樣如果以後需要將ArrayList換成LinkedList時,客戶端代碼不用改變。這就是針對抽象編程。

 

一、系統簡述:

1.集合框架總體結構

   Java中集合類定義主要是java.util.*包下面,常用的集合在系統中定義了三大接口,這三類的區別是

java.util.Set接口及其子類,set提供的是一個無序的集合;

java.util.List接口及其子類,List提供的是一個有序的集合;

java.util.Map接口及其子類,Map提供了一個映射(對應)關係的集合數據結構;

另外,在JDK5中新增了Queue(隊列)接口及其子類,提供了於隊列的集合體系。每種集合,都可以理解爲用來在內存中存放一組對象的某種容器“---就像數組,就像前面我們自己定義的隊列。

2.Set接口和List接口

Set 是最簡單的一種集合,它的對象不按特定方式排序,只是簡單的把對象加入集合中,就像往口袋裏放東西。對集中成員的訪問和操作是通過集中對象的引用進行的,所以集中不能有重複對象;而LIst的主要特徵是其對象以線性方式存儲,沒有特定順序,只有一個開頭和一個結尾,當然,它與根本沒有順序的集是不同的。列表在數據結構中分別表現爲:數組和向量、鏈表、堆棧、隊列。關於實現列表的集合類,是我們日常工作中經常用到的;從類圖中可以看到,Set 和List都是繼承了Collection接口的子接口,而這類集合都有自己的實現;

3.java.util.Map接口

現實生活中,我們常會看到這樣的一種集合:IP地址與主機名,身份證號與個人,系統用戶用與系統用戶對象等,這種一一對應的關係,就叫做映射。Java提供了專門的集合類用來存放這種對象關係的對象,即java.util.Map接口。Map是一個接口,有多種具體的實現類,常用的有HashMap和 Hashtable類實現。Map中存入的對象是一對一對的,即每個對象和它的一個名字(鍵)關聯在一起,Map中數據存放的結構如下圖示:

Key(鍵或名)

Value(key對應的值)

“userName1”

UserInfo對象1

“userName1”

UserInfo對象1

“userName1”

UserInfo對象1

     . . .

. . .

可以看出,Map中存放的是兩種對象,一種稱爲key(),一種稱爲value(),它們在在Map中是一一對應關係,這一對對象又稱做Map中的一個Entry()Map中的鍵不能重複,但值可以重複。(借鑑藍傑,>>>)

二、詳細闡述:

 

一、概述

    數據結構對程序設計有着深遠的影響,在面向過程的C語言中,數據庫結構用struct來描述,而在面向對象的編程中,數據結構是用類來描述的,並且包含有對該數據結構操作的方法。

    在Java語言中,Java語言的設計者對常用的數據結構和算法做了一些規範(接口)和實現(具體實現接口的類)。所有抽象出來的數據結構和操作(算法)統稱爲Java集合框架(Java Collection Framework)。

    Java程序員在具體應用時,不必考慮數據結構和算法實現細節,只需要用這些類創建出來一些對象,然後直接應用就可以了。這樣就大大提高了編程效率。

 

二、集合框架的層次結構

Collection是集合接口

|――――Set子接口:無序,不允許重複。

|――――List子接口:有序,可以有重複元素。

區別:Collections是集合類

Set和List對比:

Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。

List:和數組類似,List可以動態增長,查找元素效率高,插入刪除元素效率低,因爲會引起其他元素位置改變。

 

Set和List具體子類:

Set

|――――HashSet:以哈希表的形式存放元素,插入刪除速度很快。

List

|――――ArrayList:動態數組

|――――LinkedList:鏈表、隊列、堆棧。

Array和java.util.Vector

Vector是一種老的動態數組,是線程同步的,效率很低,一般不贊成使用。

 

三、Iterator迭代器(接口)

    Iterator是獲取集合中元素的過程,實際上幫助獲取集合中的元素。

    迭代器代替了 Java Collections Framework 中的 Enumeration。迭代器與枚舉有兩點不同:

    迭代器允許調用方利用定義良好的語義在迭代期間從迭代器所指向的集合移除元素。

方法名稱得到了改進。

    Iterator 僅有一個子接口ListIterator,是列表迭代器,允許程序員按任一方向遍歷列表、迭代期間修改列表,並獲得迭代器在列表中的當前位置。 ListIterator 沒有當前元素;它的光標位置 始終位於調用 previous() 所返回的元素和調用 next() 所返回的元素之間。在長度爲 n 的列表中,有 n+1 個有效的索引值,從 0 到 n(包含)。

 

四、集合框架之外的Map接口

    Map將鍵映射到值的對象。一個映射不能包含重複的鍵;每個鍵最多隻能映射一個值。

Map接口是Dictionary(字典)抽象類的替代品。

    Map 接口提供三種collection 視圖,允許以鍵集、值集合或鍵-值映射關係集的形式查看某個映射的內容。映射的順序 定義爲迭代器在映射的 collection 視圖中返回其元素的順序。某些映射實現可明確保證其順序,如 TreeMap 類;某些映射實現則不保證順序,如 HashMap 類。

    有兩個常見的已實現的子類:

HashMap:基於哈希表的 Map 接口的實現。此實現提供所有可選的映射操作,並允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。

TreeMap:它實現SortedMap 接口的基於紅黑樹的實現。此類保證了映射按照升序順序排列關鍵字,根據使用的構造方法不同,可能會按照鍵的類的自然順序 進行排序(參見 Comparable),或者按照創建時所提供的比較器進行排序。

Hashtable:此類實現一個哈希表,該哈希表將鍵映射到相應的值。任何非 null 對象都可以用作鍵或值。

 

五、線程安全類

    在集合框架中,有些類是線程安全的,這些都是JDK1.1中的出現的。在JDK1.2之後,就出現許許多多非線程安全的類。

    下面是這些線程安全的同步的類:

Vector:就比ArrayList多了個同步化機制(線程安全)。

Statck:堆棧類,先進後出。

Hashtable:就比HashMap多了個線程安全。

Enumeration:枚舉,相當於迭代器。

    除了這些之外,其他的都是非線程安全的類和接口。

   線程安全的類其方法是同步的,每次只能一個訪問。是重量級對象,效率較低。對於非線程安全的類和接口,在多線程中需要程序員自己處理線程安全問題。

 

六、其他一些接口和類介紹

Dictionary和Hashtable類:

Dictionary提供鍵值映射的功能,是個抽象類。一般使用它的子類HashTable類。遍歷Hashtable類要用到枚舉。

Properties類

Properties 繼承於 Hashtable,Properties 類表示了一個持久的屬性集。Properties 可保存在流中或從流中加載。屬性列表中每個鍵及其對應值都是一個字符串。一般可以通過讀取properties配置文件來填充Properties對象。

 

七、各個接口的闡述

1.Collection接口 

Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些 Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。 

所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用於創建一個空的Collection,有一個 Collection參數的構造函數用於創建一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個構造函數允許用戶複製一個Collection。 

如何遍歷Collection中的每一個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下: 

Iterator it = collection.iterator(); // 獲得一個迭代子 

while(it.hasNext()) { 

Object obj = it.next(); // 得到下一個元素 

由Collection接口派生的兩個接口是List和Set。 

 

2.List接口 

List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似於數組下標)來訪問List中的元素,這類似於Java的數組。 

和下面要提到的Set不同,List允許有相同的元素。 

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

實現List接口的常用類有LinkedList,ArrayList,Vector和Stack。 

 

3.LinkedList類 

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

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

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

 

4.ArrayList類 

ArrayList實現了可變大小的數組。它允許所有元素,包括null。ArrayList沒有同步。 

size,isEmpty,get,set方法運行時間爲常數。但是add方法開銷爲分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間爲線性。 

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

和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。 

 

5.Vector類 

Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的 Iterator是同一接口,但是,因爲Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。 

 

6.Stack 類 

Stack繼承自Vector,實現一個後進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否爲空,search方法檢測一個元素在堆棧中的位置。Stack剛創建後是空棧。 

 

7.Set接口 

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

很明顯,Set的構造函數有一個約束條件,傳入的Collection參數不能包含重複的元素。 

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

 

8.Map接口 

請注意,Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每個key只能映射一個 value。Map接口提供3種集合的視圖,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value映射。 

 

9.Hashtable類 

Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null)的對象都可作爲key或者value。 

添加數據使用put(key, value),取出數據使用get(key),這兩個基本操作的時間開銷爲常數。 

Hashtable 通過initial capacity和load factor兩個參數調整性能。通常缺省的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查找時間將增大,這會影響像get和put這樣的操作。 

使用Hashtable的簡單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”: 

Hashtable numbers = new Hashtable(); 

numbers.put(“one”, new Integer(1)); 

numbers.put(“two”, new Integer(2)); 

numbers.put(“three”, new Integer(3)); 

要取出一個數,比如2,用相應的key: 

Integer n = (Integer)numbers.get(“two”); 

System.out.println(“two = ” + n); 

由於作爲key的對象將通過計算其散列函數來確定與之對應的value的位置,因此任何作爲key的對象都必須實現hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同,如果兩個不同對象的hashCode相同,這種現象稱爲衝突,衝突會導致操作哈希表的時間開銷增大,所以儘量定義好的hashCode()方法,能加快哈希表的操作。 

如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法返回null),要避免這種問題,只需要牢記一條:要同時複寫equals方法和hashCode方法,而不要只寫其中一個。

轉自:CSDN

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章