java零基礎(chǔ)入門-高級特性篇(四)? HashSet 和 Collections
本章繼續(xù)講集合,先來看看Set集合。Set集合的特點,1:無序,2:無重復(fù)。上一章講了HashMap,最后提到HashSet的底層實現(xiàn)其實就是HashMap。那么為什么用HashMap就可以實現(xiàn)無序和不重復(fù),下面看看具體如何使用HashMap實現(xiàn)HashSet。
hashset和hashmap的區(qū)別
hashset底層用hashmap實現(xiàn),那為什么不直接用hashmap就完了,非要整個hashset出來?

如果有這個問題,可以回頭看看前面講的集合框架的設(shè)計。設(shè)計hashset是用來保存那種不需要使用下標(biāo)操作元素,并且不能重復(fù)的集合。set集合的元素和List集合的元素一樣,都是一個對象。而hashmap的元素是key-value鍵值對,因為數(shù)據(jù)存儲類型不同,所以需要將set和map區(qū)分開來??匆幌聢D,幫助回憶一下。
hashmap如何實現(xiàn)hashset
Set集合最重要的一個方法就是add(E e),如何往一個Set集合添加元素,了解了添加元素的原理,查找元素理解起來就簡單許多。hashset的add方法,用的就是是hashmap的put方法。下面是源代碼:
map.put(e,PRESENT) == null
這里需要重點理解的是,一個對象如何存入一個key-value。從上面這句代碼中,可以發(fā)現(xiàn),在往set集合添加元素的時候,這個元素被用來當(dāng)做map的key,而value是一個常量。
為什么直接將對象作為key呢?因為hashmap使用哈希算法對key進(jìn)行計算,計算后的結(jié)果就是底層數(shù)組的位置,所以當(dāng)使用hashset的時候,需要對放進(jìn)set的對象進(jìn)行哈希計算,至于value,hashset不關(guān)心。
hashmap的key的特性就是不會重復(fù),后添加相同的數(shù)據(jù)會將前一個數(shù)據(jù)覆蓋掉。正好滿足了set集合不重復(fù)的特性,所以直接用hashmap即可以滿足hashset集合的要求。

其實這里容易繞暈的是幾個底層實現(xiàn)的結(jié)構(gòu),這里用一個圖來說明一下。HashSet利用HashMap的Key的特性來實現(xiàn),而HashMap是利用數(shù)組和鏈表的特性來實現(xiàn),這樣應(yīng)該明白這幾個結(jié)構(gòu)之間的關(guān)系了吧。
這次hashset真的講完了。
Collections工具
使用集合存放數(shù)據(jù)的時候,會有很多情況要對集合進(jìn)行操作。特別是List集合,因為List集合的有序性,會需要按照特定的順序操作集合,而java也專門提供了Collections工具來對集合進(jìn)行操作。下面來看看幾個例子
List list = new ArrayList();
list.add("one");//此處省略 ,一共添加五個元素one,two,three,fore,five

Collection.reverse(list);//反轉(zhuǎn)集合元素的順序
Collection.shuffle(list);//隨機順序,多次隨機結(jié)果可能不一樣
Collection.sort(list);//升序排序,先數(shù)字后字母,數(shù)字0-9,字母A-Z a-z的順序,逐位比較
Collection.swap(list,0,3);//交換第一個和第四個元素位置
看一個swap的源碼實現(xiàn)
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
嗯,完了,是不是有一種這玩意我都會寫,干嘛要用這個的感覺?其實確實可以自己寫,但是一般提供工具給別人用,肯定要提供全套,再就是合理的使用工具會減少不必要的代碼,提升代碼的可讀性。
Collection 和 Collections
這兩個長得很像,但是作用差別很大,初學(xué)者切勿將兩者概念混淆。Collection是集合體系中的上層接口,而Collections是操作集合的工具。何謂工具?還記不記得我們講的靜態(tài)方法?不記得的快去復(fù)習(xí)類和對象的文章。
Collections作為一個工具類,里面提供的方法都是靜態(tài)方法,所以在上面的例子中,都是直接使用類Collections來調(diào)用方法,Collections提供了大量的靜態(tài)方法來操作集合,有沒有加深對靜態(tài)成員這個概念的理解?
用Collections工具類創(chuàng)建線程安全的集合
上次講vector的時候,說了他是線程安全的集合,而List是線程不安全的。但是可以通過一些方法讓List變成線程安全的,所以vector目前已經(jīng)沒有使用的必要了。那么如何讓List變成線程安全的集合呢?答案就是使用Collections工具可以將List變?yōu)榫€程安全。
Collections有一系列的synchronized方法來使集合變?yōu)榫€程安全的,一系列是指不僅僅可以將List變?yōu)榫€程安全的,Set,Map也有方法變成線程安全的。
List list = Collections.synchronizedList(new ArrayList());
Set set = Collections.synchronizedSet(new HashSet());
Map map = Collections.synchronizedMap(new HashMap());
使用以上三個工具方法就可以將普通集合變?yōu)榫€程安全的集合。這些方法有什么魔法么,為什么外面包一層就線程安全了?下面來簡單介紹一下多線程以及使用多線程會遇到的問題。
多線程是什么?
多線程就是并行處理問題。比如去銀行取錢,如果只有一個取款機隊伍就會排很長,但是如果有多個取款機同時辦理業(yè)務(wù),速度就會快很多。這就是多線程的思路,多個線程(取款機)處理一個問題(取錢)。

為什么多線程有安全問題?
假設(shè)現(xiàn)在有2個人要取款,如果2個人同時操作一個取款機會發(fā)生什么?第一個人密碼輸了3位數(shù),第二個人跑來按3位數(shù),第一個人刪了準(zhǔn)備重新輸,又被第二個人按了3下,這樣下去兩個人都別想取錢。多個線程搶同一個資源就會產(chǎn)生線程安全問題,實際開發(fā)中遇到的線程安全問題會比這種情況還要復(fù)雜。
怎么解決?

新款取款機,帶門帶鎖的!一旦一個人進(jìn)去了,先鎖門,然后就可以放心大膽的取錢了,這時候沒有人會來跟你搶著取錢了。代碼里面也是可以上鎖的,所以線程安全的集合都是帶有鎖機制的。
高效并發(fā)容器
這里是補充知識,了解即可。其實上面的這幾個方法確實可以將普通集合轉(zhuǎn)為線程安全的集合,但是實現(xiàn)很粗糙,導(dǎo)致效率不是很高。所以就有了專門為并發(fā)情況設(shè)計的更加高效的并發(fā)容器,比如CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap。
上鎖也是有很多種上鎖的方法,繼續(xù)取錢的例子。
最極端粗暴的上鎖方式就是,銀行每次只準(zhǔn)進(jìn)一個人,這樣絕對不會有任何問題,絕對安全,銀行所有保安都盯著你,你還能玩出花來?但是這樣做效率極端底下。比如老版本的vector和hashtable就是用類似這種粗暴的方法來上鎖。
再來看稍微先進(jìn)點的上鎖方式。就是銀行可以進(jìn)很多人,但是辦理不同業(yè)務(wù)的分開來,取錢的一個隊,存錢的一個隊,辦理財換密碼再來一個隊,每個隊同時只能一個人辦,辦的人進(jìn)小房間,上鎖。這種上鎖的粒度比上面那種要小,效率要快很多。
最后就是最先進(jìn)的鎖了。也就是類似并發(fā)容器這種,升級版的取款機不僅可以取錢還可以存錢,還能辦其他業(yè)務(wù)~也就是說不管辦什么業(yè)務(wù),找個人最少的隊排著就行了,所有機器可以同時辦相同或者不同的業(yè)務(wù)。這種鎖的粒度最小,只對操作對象的數(shù)據(jù)進(jìn)行上鎖,效率最高。