集合架構

[java]
view plaincopyprint?
<span style="white-space:pre"> </span>//arraylist --- 實作了list接口,arraylist是容量大小可變的數組的實作
arraylist al = new arraylist();
//将元素添加到al集合中的尾部
string str1 = "a";
al.add(str1);
string str2 = "b";
al.add(str2);
//将元素添加到al集合中的指定位置
string str3 = "c";
al.add(0, str3);
//可以添加重複元素
//按下标移除元素
al.remove(0);
//按元素移除集合中指定元素,ps:如果該元素在集合中不止一個,移除的元素是正序第一個比對到的元素
al.remove(str2);
//周遊輸出
for(int i = 0; i < al.size(); i++){
//按下标擷取元素,ps:因為get()方法傳回的是object類型的資料,是以要強制類型轉換
string tmp = (string)al.get(i);
system.out.print(tmp + " ");
}
system.out.println();
//擦除集合中所有元素
al.removeall(al);
<span style="white-space:pre"> </span>//linkedlist --- 實作的接口比較多,用的較多的是它的雙端隊列的特性
linkedlist ll = new linkedlist();
//上述arraylist所使用的方法linkedlist都能使用
//除此之外,還有以下這些
//将元素添加到集合的首部
ll.addfirst(str1);
//将元素添加到集合的尾部
ll.addlast(str2);
ll.addlast(str3);
ll.addlast(str1);
//移除集合首部元素
ll.removefirst();
ll.remove();
//移除集合尾部元素
ll.removelast();
//移除與指定元素相同的,從首部到尾部之間第一個元素
ll.removefirstoccurrence(str1);
//移除與指定元素相同的,從首部到尾部之間最後一個元素
ll.removelastoccurrence(str2);
//擷取首部元素
ll.getfirst();
//擷取尾部元素
ll.getlast();
//移除所有元素
ll.removeall(ll);
<span style="white-space:pre"> </span>//vector --- vector可以實作可增長的對象數組,vector的大小可以根據需要增大或縮小,以适應建立 vector後進行添加或移除項的操作。
vector v = new vector();
//上述arraylist所使用的方法vector都能使用
//添加一個元件到向量向量末尾
v.addelement(str1);
v.addelement(str2);
//傳回向量目前的大小
v.capacity();
//判斷向量是否為空
v.isempty();
//擷取首部元件
v.firstelement();
//擷取尾部元件
v.lastelement();
//擴大該向量的大小
v.ensurecapacity(5);
//移除指定位置的元件
v.removeelementat(0);
//移除正序第一個與指定元件相同的元件
v.removeelement(str1);
//移除所有元件,并将向量大小設定為0
v.removeallelements();
<span style="white-space:pre"> </span>//stack --- stack表示棧結構。繼承至 vector。
//它提供了push和 pop操作,以及取堆棧頂點的 peek方法、測試棧是否為空的empty方法、在棧中查找項并确定到棧頂距離的search方法。
stack s = new stack();
//壓棧方法
s.push(str1);
s.push(str2);
s.push(str3);
//取棧頂元素方法
s.peek();
//出棧方法
s.pop();
//測試堆棧是否為空的方法
s.empty();
//在棧中查找項并确定到棧頂距離的方法,ps:傳回值以1為棧頂基數,-1為未找到
system.out.println(s.search(str1));
arraylist與vector都是java的集合類,都可以用來存放java對象,這是他們的相同點,但是他們也有差別:
1、同步性
vector是線程同步的。這個類中的一些方法保證了vector中的對象是線程安全的。而arraylist則是線程異步的,是以arraylist中的對象并不是線程安全的。因為同步的要求會影響執行的效率,是以如果你不需要線程安全的集合那麼使用arraylist是一個很好的選擇,這樣可以避免由于同步帶來的不必要的性能開銷。
2、資料增長
從内部實作機制來講arraylist和vector都是使用數組(array)來控制集合中的對象。當你向這兩種類型中增加元素的時候,如果元素的數目超出了内部數組目前的長度它們都需要擴充内部數組的長度,vector預設情況下自動增長原來一倍的數組長度,arraylist是原來的50%,是以最後你獲得的這個集合所占的空間總是比你實際需要的要大。是以如果你要在集合中儲存大量的資料那麼使用vector有一些優勢,因為你可以通過設定集合的初始化大小來避免不必要的資源開銷。
<span style="white-space:pre"> </span>//map結構常用的集合類
//hashmap --- 此實作假定哈希函數将元素适當地分布在各桶之間,其取出和存入的時間複雜度非常低,為常數級别,ps:不同存在相同鍵
hashmap hm = new hashmap();
//放入鍵值對
int num1 = 1;
hm.put(num1, str1);
//如果鍵以存在,則替換該鍵值對
hm.put(num1, str2);
int num2 = 2;
hm.put(num2, str2);
int num3 = 3;
hm.put(num3, str3);
//通過鍵擷取相應值
hm.get(num1);
//通過鍵判斷集合是否存在該鍵值對
hm.containskey(num1);
//通過值判斷集合是否存在該鍵值對
hm.containsvalue(str1);
//利用疊代器周遊集合中的所有鍵值對
//keyset()傳回此映射中所包含的鍵的set視圖
//iterator()傳回在此 set中的元素上進行疊代的疊代器
iterator it = hm.keyset().iterator();
//hasnext()如果仍有元素可以疊代,則傳回 true。
while(it.hasnext()){
//取出key
//next()傳回疊代的下一個元素。
//tostring()傳回string類型資料,ps:這裡不能強制類型轉換成string
int key = integer.parseint(it.next().tostring());
//通過key擷取value
string tmp = (string)hm.get(key);
//輸出value
<span style="white-space:pre"> </span>//hashtable --- hashtable具有同步性,線程安全
//hashtable在用法上和hashmap基本相似
hashtable ht = new hashtable();
//放入空值會報錯
//ht.put(null, null);
//測試此哈希表是否沒有鍵映射到值。
ht.isempty();
hashmap與hashtable都是java的集合類,都可以用來存放java對象,這是他們的相同點,但是他們也有差別。
1、曆史原因
hashtable是基于陳舊的dictionary類的,hashmap是java 1.2引進的map接口的一個實作。
2、同步性
hashtable是線程同步的。這個類中的一些方法保證了hashtable中的對象是線程安全的。而hashmap則是線程異步的,是以hashmap中的對象并不是線程安全的。因為同步的要求會影響執行的效率,是以如果你不需要線程安全的集合那麼使用hashmap是一個很好的選擇,這樣可以避免由于同步帶來的不必要的性能開銷,進而提高效率。
3、值
hashmap可以讓你将空值作為一個表的條目的key或value但是hashtable是不能放入空值的(null)
java的設計者給我們提供了這些集合類,在後面程式設計中是相當有用的,具體什麼時候用什麼集合,要根據我們剛才分析的集合異同來選取。
如何選用集合類?
1、要求線程安全,使用vector、hashtable
2、不要求線程安全,使用arraylist,linkedlist,hashmap
3、要求key和value鍵值,則使用hashmap,hashtable
4、資料量很大,又要線程安全,則使用vector
hashset是基于hashmap實作的,hashset底層采用hashmap來儲存所有元素。
hashcode和equal()是hashmap用的,因為無需排序是以隻需要關注定位和唯一性即可
hashcode是用來計算hash值的,hash值是用來确定hash表索引的
hash表中的一個索引存放的是一張連結清單,是以還要通過equal方法循環比較鍊上的每一個對象才可以真正定位到鍵值對應的entry
put時,如果hash表中沒定位到,就在連結清單前加一個entry,如果定位到了,則更換entry中的value(值)并傳回舊value(值)
覆寫key的hashcode()和equal()時一定要注意,不要把它們和可變屬性關聯上,否則屬性變了之後hashcode會變,equal也會為false,這樣在map中就找不到它了而且這樣的對象因為找不到它是以得不到釋放,這樣就變成了一個無效引用(相當于記憶體洩漏)
<span style="white-space:pre"> </span>//set結構常用的集合類
//hashset --- 以hashmap為底層
//hashset中無get方法,隻能通過疊代器擷取鍵
hashset hs = new hashset();
//添加鍵到集合尾部
hs.add(str1);
hs.add(str2);
hs.add(str3);
//自動去掉重複元素,ps:其實也是像hashmap一樣,用新鍵覆寫舊鍵
//傳回包含全部鍵的object類型數組
object[] o = hs.toarray();
for(int i = 0; i < o.length; i++){
system.out.print(o[i] + " ");
treeset集合類是一個有序集合,它的元素按照升序排序,預設是自然順序排列,也就是說
treeset中的對象元素需要實作comparable接口。treeset與hashset類一樣沒有get()方法來擷取清單中的元素,是以也隻能通過疊代器方法來擷取。
由于treemap需要排序,是以需要一個comparator為鍵值進行大小比較,當然也是用comparator定位的comparator可以在建立treemap時指定,這時排序時使用comparator.compare
如果建立時沒有指定comparator那麼就會使用key.compareto()方法,這就要求key必須實作comparable接口
treemap是使用tree資料結構實作的,是以使用compare接口就可以完成定位了。
treeset是依靠treemap來實作的。
<span style="white-space:pre"> </span>//treeset --- 使用元素的自然順序對元素進行排序,ps:自然順序是升序,或者根據建立 set時提供的 comparator進行排序,具體取決于使用的構造方法
treeset ts = new treeset();
//添加鍵到集合
ts.add(str1);
ts.add(str2);
ts.add(str3);
//疊代器周遊
for(it = ts.iterator(); it.hasnext();){
string tmp = it.next().tostring();
//傳回此 set 中小于等于給定元素的最大元素;如果不存在這樣的元素,則傳回 null。
ts.floor(str2);
//傳回此 set 中大于等于給定元素的最小元素;如果不存在這樣的元素,則傳回 null。
ts.ceiling(str2);
//傳回此 set 中嚴格小于給定元素的最大元素;如果不存在這樣的元素,則傳回 null。
ts.lower(str2);
//傳回此 set 中嚴格大于給定元素的最小元素;如果不存在這樣的元素,則傳回 null。
ts.higher(str2);
//擷取并移除第一個(最低)元素;如果此 set為空,則傳回 null。
ts.pollfirst();
//擷取并移除最後一個(最高)元素;如果此 set為空,則傳回 null。
ts.polllast();
hashset是基于hash算法實作的,性能優于treeset。通常使用hashset,在我們需要對其中元素排序的時候才使用treeset。
queue接口與list、set同一級别,都是繼承了collection接口。linkedlist實作了queue接口。queue接口窄化了對linkedlist的方法的通路權限(即在方法中的參數類型如果是queue時,就完全隻能通路queue接口所定義的方法了,而不能直接通路 linkedlist的非queue的方法),以使得隻有恰當的方法才可以使用。blockingqueue繼承了queue接口。
隊列是一種資料結構。它有兩個基本操作:在隊列尾部加人一個元素,和從隊列頭部移除一個元素。就是說,隊列以一種先進先出的方式管理資料,如果你試圖向一個已經滿了的阻塞隊列中添加一個元素或者是從一個空的阻塞隊列中移除一個元索,将導緻線程阻塞。
在多線程進行合作時,阻塞隊列是很有用的工具。工作者線程可以定期地把中間結果存到阻塞隊列中而其他工作者線線程把中間結果取出并在将來修改它們。隊列會自動平衡負載。如果第一個線程集運作得比第二個慢,則第二個線程集在等待結果時就會阻塞。如果第一個線程集運作得快,那麼它将等待第二個線程集趕上來。
add
增加一個元索
如果隊列已滿,則抛出一個iiiegaislabeepeplian異常
remove
移除并傳回隊列頭部的元素
如果隊列為空,則抛出一個nosuchelementexception異常
element
傳回隊列頭部的元素
offer
添加一個元素并傳回true
如果隊列已滿,則傳回false
poll
移除并返問隊列頭部的元素
如果隊列為空,則傳回null
peek
put
添加一個元素
如果隊列滿,則阻塞
take
如果隊列為空,則阻塞
remove、element、offer 、poll、peek 其實是屬于queue接口。
阻塞隊列的操作可以根據它們的響應方式分為以下三類:add、remove和element操作在你試圖為一個已滿的隊列增加元素或從空隊列取得元素時 抛出異常。當然,在多線程程式中,隊列在任何時間都可能變成滿的或空的,是以你可能想使用offer、poll、peek方法。這些方法在無法完成任務時 隻是給出一個出錯示而不會抛出異常。
注意:poll和peek方法出錯進傳回null。是以,向隊列中插入null值是不合法的。
一些隊列有大小限制,是以如果想在一個滿的隊列中加入一個新項,多出的項就會被拒絕。這時新的 offer 方法就可以起作用了。它不是對調用 add() 方法抛出一個 unchecked 異常,而隻是得到由 offer() 傳回的 false。
remove()和poll()方法都是從隊列中删除第一個元素(head)。remove()的行為與collection 接口的版本相似,但是新的 poll()方法在用空集合調用時不是抛出異常,隻是傳回null。是以新的方法更适合容易出現異常條件的情況。
element()和peek()用于在隊列的頭部查詢元素。與 remove()方法類似,在隊列為空時,element()抛出一個異常,而peek()傳回null。
一個由數組支援的有界隊列。基于數組的阻塞循環隊列,先進先出原則對元素進行排序。
一個由連結節點支援的可選有界隊列。基于連結清單的隊列,先進先出排序元素。
一個由優先級堆支援的無界優先級隊列。priorityblockingqueue是對priorityqueue的再次包裝,是基于堆資料結構的,而priorityqueue是沒有容量限制的,與arraylist一樣,是以在優先阻塞隊列上put時是不會受阻的。但是由于資源被耗盡,是以試圖執行添加操作可能會導緻outofmemoryerror),但是如果隊列為空,那麼取元素的操作take就會阻塞,是以它的檢索操作take是受阻的。另外,往入該隊列中的元素要具有比較能力。
一個由優先級堆支援的、基于時間的排程隊列。(基于priorityqueue來實作的)是一個存放delayed 元素的無界阻塞隊列,隻有在延遲期滿時才能從中提取元素。該隊列的頭部是延遲期滿後儲存時間最長的 delayed 元素。如果延遲都還沒有期滿,則隊列沒有頭部,并且poll将傳回null。當一個元素的 getdelay(timeunit.nanoseconds) 方法傳回一個小于或等于零的值時,則出現期滿,poll就以移除這個元素了。此隊列不允許使用null元素。
一個利用 blockingqueue接口的簡單聚集(rendezvous)機制。
synchronousqueue 類是最簡單的。它沒有内部容量。它就像線程之間的手遞手機制。在隊列中加入一個元素的生産者會等待另一個線程的消費者。當這個消費者出現時,這個元素就直接在消費者和生産者之間傳遞,永遠不會加入到阻塞隊列中。
注意:queue隊列是不能直接執行個體化的。
list按對象進入的順序儲存對象,不做排序和編輯操作。
set對每個對象隻接受一次,并使用自己内部的排序方法(通常,你隻關心某個元素是否屬于set而不關心它的順序--否則使用list)。
map同樣對每個元素儲存一份,但這是基于"鍵"(key)的,map也有内置的排序,因而不關心元素添加的順序。
如果添加元素的順序對程式設計很重要,應該使用linkedhashset或者linkedhashmap。
實際上有兩種list:一種是基本的arraylist其優點在于随機通路元素,另一種是更強大的linkedlist它并不是為快速随機通路設計的,而是具有一套更通用的方法。
list:次序是list最重要的特點:它保證維護元素特定的順序。list為collection添加了許多方法,使得能夠向list中間插入與移除元素(這隻推薦linkedlist使用)一個list可以生成listlterator,使用它可以從兩個方向周遊list,也可以從list中間插入和移除元素。
arraylist:由數組實作的list。允許對元素進行快速随機通路,但是向list中間插入與移除元素的速率很慢。listlterator隻應該用來由後向前周遊arraylist。而不是用來插入和移除元素。因為那比linkedlist開銷要大很多。
linkedlist:對順序通路進行了優化,向list中間插入與删除的開銷并不大。随機通路則相對較慢。(使用arraylist代替)還具有下列方法:addfirst(),addlast(),getfirst(),getlast(),removefirst()和removelast()這些方法(沒有在任何接口或基類中定義過)使得linkedlist可以當作堆棧、隊列和雙向隊列使用。
set具有與collection完全一樣的接口,是以沒有任何額外的功能,不象前面有兩個不同的list。實際上set就是collection,隻是行為不同。(這是繼承與多态思想的典型應用:表現不同的行為。)set不儲存重複的元素(至于如何判斷元素相同則較為負責)
set:存入set的每個元素都必須是唯一的,因為set不儲存重複元素。加入set的元素必需定義equals()方法以確定對象的唯一性。set與collection有完全一樣的接口。set接口不保證維護元素的次序。
hashset:為快速查找設計的set。存入hashset的對象必須定義hashcode()。
treeset:儲存次序的set,底層為樹結構。使用它可以從set中提取有序的序列。
linkedhashset:具有hashset的查詢速度,且内部使用連結清單維護元素的順序(插入的次序)。于是在使用疊代器周遊set時,結果會按元素插入的次序顯示。
方法put(object key,object value)添加一個"值"(想要得東西)和與"值"相關的"鍵"(key)(使用它來查找)。方法get(object key)傳回與給定"鍵"相關聯的"值"。可以用containskey()和containsvalue()測試map中是否包含某個"鍵"或"值"。标準的java類庫中包含了幾種不同的map:hashmap,treemap,linkedhashmap,weakhashmap,ldentityhashmap。它們都有同樣的基本接口map,但是行為、效率、排序政策、儲存對象的生命周期和判定"鍵"等價的政策等各不相同。
執行效率是map的一個大問題。看看get()要做哪些事,就會明白為什麼在arraylist中搜尋"鍵"是相當慢的。這正是hashmap提高速度的地方。hashmap使用了特殊的值,稱為"散列碼"(hash code),來取代對鍵的緩慢搜尋。"散列碼"是"相對唯一"用以代表對象的int值,它是通過将該對象的某些資訊進行轉換而生成的。所有java對象都能産生散列碼,因為hashcode()是定義在基類object中的方法。
hashmap就是使用對象的hashcode()進行快速查詢的。此方法能夠顯著提高性能。
map:維護"鍵值對"的關聯性,使你可通過"鍵"查找"值"
hashmap:map基于散清單的實作。插入和查詢"鍵值對"的開銷是固定的。可以通過構造器設定容量capacity和負載因子load factor,以調整容器的性能。
linkedhashmap:類似于hashmap,但是疊代周遊它時,取得"鍵值對"的順序是其插入次序,或者是最近最少使(lru)的次序。隻能hashmap慢一點。而在疊代通路時發而更快,因為它使用鍵表維護内部次序。
treemap:基于紅黑樹資料結果的實作。檢視"鍵"或"鍵值對"時,它們會被排序(次序由comparabel或comparator決定)。treemap的特點在于,你得到的結果是經過排序的。treemap是唯一的帶有submap()方法的map,它可以傳回一個子樹。