Java容器詳解(以Array Arrays ArrayList為例)
//先記錄一個想法……java實在是太臃腫了,純面向物件也有不少弊端……
//能不能把java精簡一下啊!
先上結論:
Array:認真看api索引的話,Array有兩個。一個是sql中的介面,一個是類,我們在這裡說的是這個類。
Arrays:對陣列的一些列操作。
ArrayList:一個容器。
零/概述
在學Java以前,一說到存放東西,第一個想到的就是使用陣列,使用陣列,在資料的存取方面的卻也挺方便,其儲存效率高訪問快,但是它也受到了一些限制,比如說陣列的長度以及陣列的型別,當我需要一組string型別資料的同時還需要Integer型別的話,就需要定義兩次,同時,陣列長度也受到限制,即使是動態定義陣列長度,但是長度依然需要固定在某一個範圍內,不方便也不靈活。
如果說我想要消除上面的這個限制和不方便應該怎麼辦呢?Java是否提供了相應的解決方法。答案是肯定的,這就是Java容器,java容器是javaAPI所提供的一系列類的例項,用於在程式中存放物件,主要位於Java.util包中,其長度不受限制,型別不受限制,你在存放String類的時候依然能夠存放Integer類,兩者不會衝突。
java類庫中為我們定義了很多類。可以劃分成 容器類(也稱集合類) 工具類//大體我現在就知道這三種分類……我也不知道還有沒有其他型別
集合類是來實現集合操作的;工具類是對集合功能的擴充套件,其中大部分是static函式,一般不必例項化建立物件,而直接呼叫對集合進行操作。
一/集合類
集合類存放於java.util包中。
集合類存放的都是物件的引用,而非物件本身,出於表達上的便利,我們稱集合中的物件就是指集合中物件的引用(reference)。
1.集合框架
//以上 圖一
//以上 圖二
//以上 圖三
//以上 圖四
//以上 圖五
從圖二中可以看出
集合介面:6個介面(短線表示),表示不同集合型別,是集合框架的基礎。主要是 list列表 map對映 set集 三大類。
抽象類:5個抽象類(長虛線表示),對集合介面的部分實現。可擴充套件為自定義集合類。//一般不用,一般直接使用實現類。
實現類:8個實現類(實線表示),對介面的具體實現。ArrayList HashMap 等等
在很大程度上,一旦您理解了介面,您就理解了框架。雖然您總要建立介面特定的實現,但訪問實際集合的方法應該限制在介面方法的使用上;因此,允許您更改基本的資料結構而不必改變其它程式碼。
· Collection 介面是一組允許重複的物件。
· Set 介面繼承 Collection,但不允許重複,使用自己內部的一個排列機制。
· List 介面繼承 Collection,允許重複,以元素安插的次序來放置元素,不會重新排列。
· Map介面是一組成對的鍵-值物件,即所持有的是key-value pairs。Map中不能有重複的key。擁有自己的內部排列機制。
· 容器中的元素型別都為Object。從容器取得元素時,必須把它轉換成原來的型別。
Collection是List和Set兩個介面的基介面
List在Collection之上增加了"有序"
Set在Collection之上增加了"唯一"
而ArrayList是實現List的類...所以他是有序的.它裡邊存放的元素在排列上存在一定的先後順序,而且ArrayList是採用陣列存放元素。另一種List LinkedList採用的則是連結串列。
Collection和Map介面之間的主要區別在於:Collection中儲存了一組物件,而Map儲存關鍵字/值對。在Map物件中,每一個關鍵字最多有一個關聯的值。
Map:不能包括兩個相同的鍵,一個鍵最多能繫結一個值。null可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,即可以表示Map中沒有該鍵,也可以表示該鍵所對應的值為null。因此,在Map中不能由get()方法來判斷Map中是否存在某個鍵,而應該用containsKey()方法來判斷。
繼承Map的類有:HashMap,HashTable
HashMap:Map的實現類,預設情況下是非同步的,可以通過Map Collections.synchronizedMap(Map m)來達到執行緒同步
HashTable:Dictionary的子類,預設是執行緒同步的。不允許關鍵字或值為null
當元素的順序很重要時選用TreeMap,當元素不必以特定的順序進行儲存時,使用HashMap。Hashtable的使用不被推薦,因為HashMap提供了所有類似的功能,並且速度更快。當你需要在多執行緒環境下使用時,HashMap也可以轉換為同步的
2.陣列與容器
陣列與其它容器的區別體現在三個方面:效率,型別識別以及可以持有primitives。陣列是Java提供的,能隨機儲存和訪問reference序列的諸多方法中的,最高效的一種。陣列是一個簡單的線性序列,所以它可以快速的訪問其中的元素。但是速度是有代價的;當你建立了一個數組之後,它的容量就固定了,而且在其生命週期裡不能改變。也許你會提議先建立一個數組,等到快不夠用的時候,再建立一個新的,然後將舊的數組裡的reference全部導到新的裡面。其實(我們以後會講的)ArrayList就是這麼做的。但是這種靈活性所帶來的開銷,使得ArrayList的效率比起陣列有了明顯下降。
還有一些泛型容器類包括List,Set和Map。他們處理物件的時候就好像這些物件都沒有自己的具體型別一樣。也就是說,容器將它所含的元素都看成是(Java中所有類的根類)Object的。這樣你只需要建一種容器,就能把所有型別的物件全都放進去。從這個角度來看,這種作法很不錯(只是苦了primitive。如果是常量,你還可以用Java的primitive的Wrapper類;如果是變數,那就只能放在你自己的類裡了)。與其他泛型容器相比,這裡體現陣列的第二個優勢:建立陣列的時候,你也同時指明瞭它所持有的物件的型別(這又引出了第三點--陣列可以持有primitives,而容器卻不行)。也就是說,它會在編譯的時候作型別檢查,從而防止你插入錯誤型別的物件,或者是在提取物件的時候把物件的型別給搞錯了。Java在編譯和執行時都能阻止你將一個不恰當的訊息傳給物件。所有這並不是說使用容器就有什麼危險,只是如果編譯器能夠幫你指定,那麼程式執行會更快,終端使用者也會較少收到程式執行異常的騷擾。
當你事先不知道要存放資料的個數,或者你需要一種比陣列下標存取機制更靈活的方法時,你就需要用到集合類。
3.詳細介紹
有序否 |
允許元素重複否 |
||
Collection |
否 |
是 |
|
List |
是 |
是 |
|
Set |
AbstractSet |
否 |
否 |
HashSet |
|||
TreeSet |
是(用二叉樹排序) |
||
Map |
AbstractMap |
否 |
使用key-value來對映和儲存資料,Key必須惟一,value可以重複 |
HashMap |
|||
TreeMap |
是(用二叉樹排序)
|
(1)集 (Set):口袋
集(set)是最簡單的一種集合,它的物件不按特定方式排序,只是簡單的把物件加入集合中,就像往口袋裡放東西。
對集中成員的訪問和操作是通過集中物件的引用進行的,所以集中不能有重複物件。
集也有多種變體,可以實現排序等功能,如TreeSet,它把物件新增到集中的操作將變為按照某種比較規則將其插入到有序的物件序列中。它實現的是SortedSet介面,也就是加入了物件比較的方法。通過對集中的物件迭代,我們可以得到一個升序的物件集合。
(2)列表 (List):列表
列表的主要特徵是其物件以線性方式儲存,沒有特定順序,只有一個開頭和一個結尾,當然,它與根本沒有順序的集是不同的。
列表在資料結構中分別表現為:陣列和向量、連結串列、堆疊、佇列。
關於實現列表的集合類,是我們日常工作中經常用到的,將在後邊的筆記詳細介紹。
(3)對映 (Map):鍵值對
對映與集或列表有明顯區別,對映中每個項都是成對的。對映中儲存的每個物件都有一個相關的關鍵字(Key)物件,關鍵字決定了物件在對映中的儲存位置,檢索物件時必須提供相應的關鍵字,就像在字典中查單詞一樣。關鍵字應該是唯一的。
關鍵字本身並不能決定物件的儲存位置,它需要對過一種雜湊(hashing)技術來處理,產生一個被稱作雜湊碼(hash code)的整數值,雜湊碼通常用作一個偏置量,該偏置量是相對於分配給對映的記憶體區域起始位置的,由此確定關鍵字/物件對的儲存位置。理想情況下,雜湊處理應該產生給定範圍內均勻分佈的值,而且每個關鍵字應得到不同的雜湊碼。
集合類簡介
java.util中共有13個類可用於管理集合物件,它們支援集、列表或對映等集合,以下是這些類的簡單介紹
集:
HashSet: 使用HashMap的一個集的實現。雖然集定義成無序,但必須存在某種方法能相當高效地找到一個物件。使用一個HashMap物件實現集的儲存和檢索操作是在固定時間內實現的.
TreeSet: 在集中以升序對物件排序的集的實現。這意味著從一個TreeSet物件獲得第一個迭代器將按升序提供物件。TreeSet類使用了一個TreeMap.
列表:
Vector: 實現一個類似陣列一樣的表,自動增加容量來容納你所需的元素。使用下標儲存和檢索物件就象在一個標準的陣列中一樣。你也可以用一個迭代器從一個Vector中檢索物件。Vector是唯一的同步容器類??當兩個或多個執行緒同時訪問時也是效能良好的。(同步的含義:即同時只能一個程序訪問,其他等待)
Stack: 這個類從Vector派生而來,並且增加了方法實現棧??一種後進先出的儲存結構。
LinkedList: 實現一個連結串列。由這個類定義的連結串列也可以像棧或佇列一樣被使用。
ArrayList: 實現一個數組,它的規模可變並且能像連結串列一樣被訪問。它提供的功能類似Vector類但不同步。
對映:
HashTable: 實現一個映象,所有的鍵必須非空。為了能高效的工作,定義鍵的類必須實現hashcode()方法和equal()方法。這個類是前面java實現的一個繼承,並且通常能在實現映象的其他類中更好的使用。
HashMap: 實現一個映象,允許儲存空物件,而且允許鍵是空(由於鍵必須是唯一的,當然只能有一個)。
WeakHashMap: 實現這樣一個映象:通常如果一個鍵對一個物件而言不再被引用,鍵/物件對將被捨棄。這與HashMap形成對照,映象中的鍵維持鍵/物件對的生命週期,儘管使用映象的程式不再有對鍵的引用,並且因此不能檢索物件。
TreeMap: 實現這樣一個映象,物件是按鍵升序排列的。
下圖是集合類所實現的介面之間的關係:
Set和List都是由公共介面Collection擴充套件而來,所以它們都可以使用一個型別為Collection的變數來引用。這就意味著任何列表或集構成的集合都可以用這種方式引用,只有對映類除外(但也不是完全排除在外,因為可以從對映獲得一個列表。)所以說,把一個列表或集傳遞給方法的標準途徑是使用Collection型別的引數。
List介面
List是有序的Collection,使用此介面能夠精確的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,這類似於Java的陣列。
和下面要提到的Set不同,List允許有相同的元素。
除了具有Collection介面必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator介面,和標準的Iterator介面相比,ListIterator多了一些add()之類的方法,允許新增,刪除,設定元素,還能向前或向後遍歷。
實現List介面的常用類有LinkedList,ArrayList,Vector和Stack。
ArrayList類
ArrayList實現了可變大小的陣列。它允許所有元素,包括null。ArrayList沒有同步。
size,isEmpty,get,set方法執行時間為常數。但是add方法開銷為分攤的常數,新增n個元素需要O(n)的時間。其他的方法執行時間為線性。
每個ArrayList例項都有一個容量(Capacity),即用於儲存元素的陣列的大小。這個容量可隨著不斷新增新元素而自動增加,但是增長演算法並沒有定義。ArrayList當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
Map介面
請注意,Map沒有繼承Collection介面,Map提供key到value的對映。一個Map中不能包含相同的key,每個key只能對映一個value。Map介面提供3種集合的檢視,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value對映。
HashMap類
HashMap和Hashtable類似,不同之處在於HashMap是非同步的,並且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的效能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。
----------------------------------------------------------------------------
1.-------------------->
List是介面,List特性就是有序,會確保以一定的順序儲存元素.
ArrayList是它的實現類,是一個用陣列實現的List.
Map是介面,Map特性就是根據一個物件查詢物件.
HashMap是它的實現類,HashMap用hash表實現的Map,就是利用物件的hashcode(hashcode()是Object的方法)進行快速(Hash)雜湊查詢.(關於雜湊查詢,可以參看<<資料結構>>)
2.-------------------->
一般情況下,如果沒有必要,推薦程式碼只同List,Map介面打交道.
比如:List list = new ArrayList();
這樣做的原因是list就相當於是一個泛型的實現,如果想改變list的型別,只需要:
List list = new LinkedList();//LinkedList也是List的實現類,也是ArrayList的兄弟類
這樣,就不需要修改其它程式碼,這就是介面程式設計的優雅之處.
另外的例子就是,在類的方法中,如下宣告:
private void doMyAction(List list){}
這樣這個方法能處理所有實現了List介面的類,一定程度上實現了泛型函式.
3.--------------------->
如果開發的時候覺得ArrayList,HashMap的效能不能滿足你的需要,可以通過實現List,Map(或者Collection)來定製你的自定義類
二/工具類——“低耦合”
其實理解了集合框架也就差不多了。工具類,正如上面說過的,是對集合功能的擴充套件,其中大部分是static函式,一般不必例項化建立物件,而直接呼叫對集合進行操作。
接下來為了方便理解,以Array Arrays ArraysList
Array:認真看api索引的話,Array有兩個。一個是sql中的介面,一個是類,我們在這裡說的是這個類。
Arrays:對陣列的一些列操作。
ArrayList是一個容器。
1)精闢闡述:
可以將 ArrayList想象成一種“會自動擴增容量的Array”。
2)Array([]):最高效;但是其容量固定且無法動態改變;
ArrayList: 容量可動態增長;但犧牲效率;
3)建議:
基於效率和型別檢驗,應儘可能使用Array,無法確定陣列大小時才使用ArrayList!
不過當你試著解決更一般化的問題時,Array的功能就可能過於受限。
4)Java中一切皆物件,Array也是物件。不論你所使用得Array型別為何,
Array名稱本身實際上是個reference,指向heap之內得某個實際物件。
這個物件可經由“Array初始化語法”被自動產生,也可以以new表示式手動產生。
5)Array可做為函式返回值,因為它本身是物件的reference;
6)物件陣列與基本型別陣列在運用上幾乎一模一樣,唯一差別在於,前者持有得是reference,後者直接持有基本型別之值;
例如:
string [] staff=new string[100];
int [] num=new int[10];
7)容器所持有的其實是一個個reference指向Object,進而才能儲存任意型別。當然這不包括基本型別,因為基本型別並不繼承自任何classes。
8)面對Array,我們可以直接持有基本型別數值的Array(例如:int [] num;),也可以持有reference(指向物件)的Array;但是容器類僅能持有reference(指向物件),若要將基本型別置於容器內,需要使用wrapper類。但是wrapper類使用起來可能不很容易上手,此外,primitives Array的效率比起“容納基本型別之外覆類(的reference)”的容器好太多了。
當然,如果你的操作物件是基本型別,而且需要在空間不足時自動擴增容量,Array便不適合,此時就得使用外覆類的容器了。
9)某些情況下,容器類即使沒有轉型至原來的型別,仍然可以運作無誤。有一種情況尤其特別:編譯器對String class提供了一些額外的支援,使它可以平滑運作。
10)對陣列的一些基本操作,像排序、搜尋與比較等是很常見的。因此在Java中提供了Arrays類協助這幾個操作:sort(),binarySearch(),equals(),fill(),asList().
不過Arrays類沒有提供刪除方法,而ArrayList中有remove()方法,不知道是否是不需要在Array中做刪除等操作的原因(因為此時應該使用連結串列)。
11)ArrayList的使用也很簡單:產生ArrayList,利用add()將物件置入,利用get(i)配合索引值將它們取出。這一切就和Array的使用方式完全相同,只不過少了[]而已。
2.參考資料:
1)效率:
陣列擴容是對ArrayList效率影響比較大的一個因素。
每當執行Add、AddRange、Insert、InsertRange等新增元素的方法,都會檢查內部陣列的容量是否不夠了,如果是,它就會以當前容量的兩倍來重新構建一個數組,將舊元素Copy到新陣列中,然後丟棄舊陣列,在這個臨界點的擴容操作,應該來說是比較影響效率的。
ArrayList是Array的複雜版本
ArrayList內部封裝了一個Object型別的陣列,從一般的意義來說,它和陣列沒有本質的差別,甚至於ArrayList的許多方法,如Index、IndexOf、Contains、Sort等都是在內部陣列的基礎上直接呼叫Array的對應方法。
2)型別識別:
ArrayList存入物件時,拋棄型別資訊,所有物件遮蔽為Object,編譯時不檢查型別,但是執行時會報錯。
ArrayList與陣列的區別主要就是由於動態增容的效率問題了
3)ArrayList可以存任何Object,如String等。
引用:
http://www.cnblogs.com/mengdd/archive/2013/01/19/2868095.html
http://www.blogjava.net/leishengwei/archive/2012/03/20/372246.html
http://my.oschina.net/wisedream/blog/137045
http://www.cnblogs.com/eflylab/archive/2007/01/20/625216.html
http://www.blogjava.net/EvanLiu/archive/2007/11/12/159884.html
http://www.cnblogs.com/wuyuegb2312/p/3867293.html
http://blog.sina.com.cn/s/blog_5ce1fe770100b0ay.html Java中Array與ArrayList的