對(duì)于HashSet而言,它是基于HashMap實(shí)現(xiàn)的,HashSet底層采用HashMap來(lái)保存所有元素,因此HashSet的實(shí)現(xiàn)比較簡(jiǎn)單,查看HashSet的源代碼,可以看到如下代碼:
public?class?HashSet<E>????extends?AbstractSet<E>????implements?Set<E>,?Cloneable,?java.io.Serializable???{????//?使用?HashMap?的?key?保存?HashSet?中所有元素???private?transient?HashMap<E,Object>?map;????//?定義一個(gè)虛擬的?Object?對(duì)象作為?HashMap?的?value????private?static?final?Object?PRESENT?=?new?Object();????...????//?初始化?HashSet,底層會(huì)初始化一個(gè)?HashMap????public?HashSet()????{????????map?=?new?HashMap<E,Object>();????}????//?以指定的?initialCapacity、loadFactor?創(chuàng)建?HashSet????//?其實(shí)就是以相應(yīng)的參數(shù)創(chuàng)建?HashMap????public?HashSet(int?initialCapacity,?float?loadFactor)????{????????map?=?new?HashMap<E,Object>(initialCapacity,?loadFactor);????}????public?HashSet(int?initialCapacity)????{????????map?=?new?HashMap<E,Object>(initialCapacity);????}????HashSet(int?initialCapacity,?float?loadFactor,?boolean?dummy)????{????????map?=?new?linkedHashMap<E,Object>(initialCapacity????????????,?loadFactor);????}????//?調(diào)用?map?的?keySet?來(lái)返回所有的?key????public?Iterator<E>?iterator()????{????????return?map.keySet().iterator();????}????//?調(diào)用?HashMap?的?size()?方法返回?Entry?的數(shù)量,就得到該?Set?里元素的個(gè)數(shù)???public?int?size()????{????????return?map.size();????}????//?調(diào)用?HashMap?的?isEmpty()?判斷該?HashSet?是否為空,???//?當(dāng)?HashMap?為空時(shí),對(duì)應(yīng)的?HashSet?也為空???public?boolean?isEmpty()????{????????return?map.isEmpty();????}????//?調(diào)用?HashMap?的?containsKey?判斷是否包含指定?key????//HashSet?的所有元素就是通過(guò)?HashMap?的?key?來(lái)保存的???public?boolean?contains(Object?o)????{????????return?map.containsKey(o);????}????//?將指定元素放入?HashSet?中,也就是將該元素作為?key?放入?HashMap????public?boolean?add(E?e)????{????????return?map.put(e,?PRESENT)?==?null;????}????//?調(diào)用?HashMap?的?remove?方法刪除指定?Entry,也就刪除了?HashSet?中對(duì)應(yīng)的元素???public?boolean?remove(Object?o)????{????????return?map.remove(o)==PRESENT;????}????//?調(diào)用?Map?的?clear?方法清空所有?Entry,也就清空了?HashSet?中所有元素???public?void?clear()????{????????map.clear();????}????...???}
由上面源程序可以看出,HashSet的實(shí)現(xiàn)其實(shí)非常簡(jiǎn)單,它只是封裝了一個(gè)HashMap對(duì)象來(lái)存儲(chǔ)所有的集合元素,所有放入HashSet中的集合元素實(shí)際上由HashMap的key來(lái)保存,而HashMap的value則存儲(chǔ)了一個(gè)PRESENT,它是一個(gè)靜態(tài)的Object對(duì)象。
HashSet的絕大部分方法都是通過(guò)調(diào)用HashMap的方法來(lái)實(shí)現(xiàn)的,因此HashSet和HashMap兩個(gè)集合在實(shí)現(xiàn)本質(zhì)上是相同的。
掌握上面理論知識(shí)之后,接下來(lái)看一個(gè)示例程序,測(cè)試一下自己是否真正掌握了HashMap和HashSet集合的功能。
?class?Name??{??????private?String?first;???????private?String?last;?????????????public?Name(String?first,?String?last)???????{???????????this.first?=?first;???????????this.last?=?last;???????}?????????public?boolean?equals(Object?o)???????{???????????if?(this?==?o)???????????{???????????????return?true;???????????}?????????????????if?(o.getClass()?==?Name.class)???????????{???????????????Name?n?=?(Name)o;???????????????return?n.first.equals(first)???????????????????&&?n.last.equals(last);???????????}???????????return?false;???????}???}????public?class?HashSetTest??{??????public?static?void?main(String[]?args)??????{???????????Set<Name>?s?=?new?HashSet<Name>();??????????s.add(new?Name("abc",?"123"));??????????System.out.println(??????????????s.contains(new?Name("abc",?"123")));??????}??}
上面程序中向HashSet里添加了一個(gè)new Name("abc","123")對(duì)象之后,立即通過(guò)程序判斷該HashSet是否包含一個(gè)new Name("abc","123")對(duì)象。粗看上去,很容易以為該程序會(huì)輸出true。
實(shí)際運(yùn)行上面程序?qū)⒖吹匠绦蜉敵鰂alse,這是因?yàn)镠ashSet判斷兩個(gè)對(duì)象相等的標(biāo)準(zhǔn)除了要求通過(guò)equals()方法比較返回true之外,還要求兩個(gè)對(duì)象的hashCode()返回值相等。而上面程序沒(méi)有重寫(xiě)Name類(lèi)的hashCode()方法,兩個(gè)Name對(duì)象的hashCode()返回值并不相同,因此HashSet會(huì)把它們當(dāng)成2個(gè)對(duì)象處理,因此程序返回false。
由此可見(jiàn),當(dāng)我們?cè)噲D把某個(gè)類(lèi)的對(duì)象當(dāng)成HashMap的key,或試圖將這個(gè)類(lèi)的對(duì)象放入HashSet中保存時(shí),重寫(xiě)該類(lèi)的equals(Object obj)方法和hashCode()方法很重要,而且這兩個(gè)方法的返回值必須保持一致:當(dāng)該類(lèi)的兩個(gè)的hashCode()返回值相同時(shí),它們通過(guò)equals()方法比較也應(yīng)該返回true。通常來(lái)說(shuō),所有參與計(jì)算hashCode()返回值的關(guān)鍵屬性,都應(yīng)該用于作為equals()比較的標(biāo)準(zhǔn)。
如下程序就正確重寫(xiě)了Name類(lèi)的hashCode()和equals()方法,程序如下:
class?Name???{???????private?String?first;??????private?String?last;??????public?Name(String?first,?String?last)??????{???????????this.first?=?first;???????????this.last?=?last;???????}???????//?根據(jù)?first?判斷兩個(gè)?Name?是否相等??????public?boolean?equals(Object?o)???????{???????????if?(this?==?o)???????????{???????????????return?true;???????????}???????????if?(o.getClass()?==?Name.class)???????????{???????????????Name?n?=?(Name)o;???????????????return?n.first.equals(first);???????????}???????????return?false;???????}??????????????//?根據(jù)?first?計(jì)算?Name?對(duì)象的?hashCode()?返回值??????public?int?hashCode()???????{???????????return?first.hashCode();???????}????????public?String?toString()???????{???????????return?"Name[first="?+?first?+?",?last="?+?last?+?"]";???????}????}???????public?class?HashSetTest2????{???????public?static?void?main(String[]?args)???????{???????????HashSet<Name>?set?=?new?HashSet<Name>();???????????set.add(new?Name("abc"?,?"123"));???????????set.add(new?Name("abc"?,?"456"));???????????System.out.println(set);???????}???}
上面程序中提供了一個(gè)Name類(lèi),該Name類(lèi)重寫(xiě)了equals()和toString()兩個(gè)方法,這兩個(gè)方法都是根據(jù)Name類(lèi)的first實(shí)例變量來(lái)判斷的,當(dāng)兩個(gè)Name對(duì)象的first實(shí)例變量相等時(shí),這兩個(gè)Name對(duì)象的hashCode()返回值也相同,通過(guò)equals()比較也會(huì)返回true。
程序主方法先將第一個(gè)Name對(duì)象添加到HashSet中,該Name對(duì)象的first實(shí)例變量值為"abc",接著程序再次試圖將一個(gè)first為"abc"的Name對(duì)象添加到HashSet中,很明顯,此時(shí)沒(méi)法將新的Name對(duì)象添加到該HashSet中,因?yàn)榇颂幵噲D添加的Name對(duì)象的first也是"abc",HashSet會(huì)判斷此處新增的Name對(duì)象與原有的Name對(duì)象相同,因此無(wú)法添加進(jìn)入,程序在①號(hào)代碼處輸出set集合時(shí)將看到該集合里只包含一個(gè)Name對(duì)象,就是第一個(gè)、last為"123"的Name對(duì)象。
以上就是深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)的小編針對(duì)“Java基礎(chǔ)內(nèi)容分享,hashset取值詳解”的內(nèi)容進(jìn)行的回答,希望對(duì)大家有所幫助,如有疑問(wèn),請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為你服務(wù)。