java零基礎入門-高級特性篇(四)? HashSet 和 Collections
本章繼續(xù)講集合如失,先來看看Set集合绊诲。Set集合的特點,1:無序褪贵,2:無重復掂之。上一章講了HashMap抗俄,最后提到HashSet的底層實現(xiàn)其實就是HashMap。那么為什么用HashMap就可以實現(xiàn)無序和不重復世舰,下面看看具體如何使用HashMap實現(xiàn)HashSet动雹。
hashset和hashmap的區(qū)別
hashset底層用hashmap實現(xiàn),那為什么不直接用hashmap就完了跟压,非要整個hashset出來胰蝠?
如果有這個問題,可以回頭看看前面講的集合框架的設計裆馒。設計hashset是用來保存那種不需要使用下標操作元素姊氓,并且不能重復的集合。set集合的元素和List集合的元素一樣喷好,都是一個對象翔横。而hashmap的元素是key-value鍵值對,因為數(shù)據(jù)存儲類型不同梗搅,所以需要將set和map區(qū)分開來禾唁。看一下圖无切,幫助回憶一下荡短。
hashmap如何實現(xiàn)hashset
Set集合最重要的一個方法就是add(E e),如何往一個Set集合添加元素哆键,了解了添加元素的原理掘托,查找元素理解起來就簡單許多。hashset的add方法籍嘹,用的就是是hashmap的put方法闪盔。下面是源代碼:
map.put(e,PRESENT) == null
這里需要重點理解的是,一個對象如何存入一個key-value辱士。從上面這句代碼中泪掀,可以發(fā)現(xiàn),在往set集合添加元素的時候颂碘,這個元素被用來當做map的key异赫,而value是一個常量。
為什么直接將對象作為key呢头岔?因為hashmap使用哈希算法對key進行計算塔拳,計算后的結果就是底層數(shù)組的位置,所以當使用hashset的時候峡竣,需要對放進set的對象進行哈希計算蝙斜,至于value,hashset不關心澎胡。
hashmap的key的特性就是不會重復孕荠,后添加相同的數(shù)據(jù)會將前一個數(shù)據(jù)覆蓋掉娩鹉。正好滿足了set集合不重復的特性,所以直接用hashmap即可以滿足hashset集合的要求稚伍。
其實這里容易繞暈的是幾個底層實現(xiàn)的結構弯予,這里用一個圖來說明一下。HashSet利用HashMap的Key的特性來實現(xiàn)个曙,而HashMap是利用數(shù)組和鏈表的特性來實現(xiàn)锈嫩,這樣應該明白這幾個結構之間的關系了吧。
這次hashset真的講完了垦搬。
Collections工具
使用集合存放數(shù)據(jù)的時候呼寸,會有很多情況要對集合進行操作。特別是List集合猴贰,因為List集合的有序性对雪,會需要按照特定的順序操作集合,而java也專門提供了Collections工具來對集合進行操作米绕。下面來看看幾個例子
List list = new ArrayList();
list.add("one");//此處省略 瑟捣,一共添加五個元素one,two栅干,three迈套,fore,five
Collection.reverse(list);//反轉集合元素的順序
Collection.shuffle(list);//隨機順序碱鳞,多次隨機結果可能不一樣
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
這兩個長得很像,但是作用差別很大污它,初學者切勿將兩者概念混淆剖踊。Collection是集合體系中的上層接口庶弃,而Collections是操作集合的工具。何謂工具德澈?還記不記得我們講的靜態(tài)方法歇攻?不記得的快去復習類和對象的文章。
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è)務舷夺,速度就會快很多。這就是多線程的思路售貌,多個線程(取款機)處理一個問題(取錢)给猾。
為什么多線程有安全問題?
假設現(xiàn)在有2個人要取款颂跨,如果2個人同時操作一個取款機會發(fā)生什么敢伸?第一個人密碼輸了3位數(shù),第二個人跑來按3位數(shù)恒削,第一個人刪了準備重新輸池颈,又被第二個人按了3下尾序,這樣下去兩個人都別想取錢。多個線程搶同一個資源就會產(chǎn)生線程安全問題饶辙,實際開發(fā)中遇到的線程安全問題會比這種情況還要復雜蹲诀。
怎么解決?
新款取款機弃揽,帶門帶鎖的脯爪!一旦一個人進去了,先鎖門矿微,然后就可以放心大膽的取錢了痕慢,這時候沒有人會來跟你搶著取錢了。代碼里面也是可以上鎖的涌矢,所以線程安全的集合都是帶有鎖機制的掖举。
高效并發(fā)容器
這里是補充知識,了解即可娜庇。其實上面的這幾個方法確實可以將普通集合轉為線程安全的集合塔次,但是實現(xiàn)很粗糙,導致效率不是很高名秀。所以就有了專門為并發(fā)情況設計的更加高效的并發(fā)容器励负,比如CopyOnWriteArrayList,CopyOnWriteArraySet匕得,ConcurrentHashMap继榆。
上鎖也是有很多種上鎖的方法,繼續(xù)取錢的例子汁掠。
最極端粗暴的上鎖方式就是略吨,銀行每次只準進一個人,這樣絕對不會有任何問題考阱,絕對安全翠忠,銀行所有保安都盯著你,你還能玩出花來乞榨?但是這樣做效率極端底下秽之。比如老版本的vector和hashtable就是用類似這種粗暴的方法來上鎖。
再來看稍微先進點的上鎖方式姜凄。就是銀行可以進很多人政溃,但是辦理不同業(yè)務的分開來趾访,取錢的一個隊态秧,存錢的一個隊,辦理財換密碼再來一個隊扼鞋,每個隊同時只能一個人辦申鱼,辦的人進小房間愤诱,上鎖。這種上鎖的粒度比上面那種要小捐友,效率要快很多淫半。
最后就是最先進的鎖了。也就是類似并發(fā)容器這種匣砖,升級版的取款機不僅可以取錢還可以存錢科吭,還能辦其他業(yè)務~也就是說不管辦什么業(yè)務,找個人最少的隊排著就行了猴鲫,所有機器可以同時辦相同或者不同的業(yè)務对人。這種鎖的粒度最小,只對操作對象的數(shù)據(jù)進行上鎖拂共,效率最高牺弄。