HashSet工作原理_动力节点Java学院整理
对于HashSet而言,它是基于HashMap实现的,HashSet底层采用HashMap来保存所有元素,因此HashSet的实现比较简单,查看HashSet的源代码,可以看到如下代码:
publicclassHashSetextendsAbstractSet implementsSet ,Cloneable,java.io.Serializable { //使用HashMap的key保存HashSet中所有元素 privatetransientHashMap map; //定义一个虚拟的Object对象作为HashMap的value privatestaticfinalObjectPRESENT=newObject(); ... //初始化HashSet,底层会初始化一个HashMap publicHashSet() { map=newHashMap (); } //以指定的initialCapacity、loadFactor创建HashSet //其实就是以相应的参数创建HashMap publicHashSet(intinitialCapacity,floatloadFactor) { map=newHashMap (initialCapacity,loadFactor); } publicHashSet(intinitialCapacity) { map=newHashMap (initialCapacity); } HashSet(intinitialCapacity,floatloadFactor,booleandummy) { map=newLinkedHashMap (initialCapacity ,loadFactor); } //调用map的keySet来返回所有的key publicIterator iterator() { returnmap.keySet().iterator(); } //调用HashMap的size()方法返回Entry的数量,就得到该Set里元素的个数 publicintsize() { returnmap.size(); } //调用HashMap的isEmpty()判断该HashSet是否为空, //当HashMap为空时,对应的HashSet也为空 publicbooleanisEmpty() { returnmap.isEmpty(); } //调用HashMap的containsKey判断是否包含指定key //HashSet的所有元素就是通过HashMap的key来保存的 publicbooleancontains(Objecto) { returnmap.containsKey(o); } //将指定元素放入HashSet中,也就是将该元素作为key放入HashMap publicbooleanadd(Ee) { returnmap.put(e,PRESENT)==null; } //调用HashMap的remove方法删除指定Entry,也就删除了HashSet中对应的元素 publicbooleanremove(Objecto) { returnmap.remove(o)==PRESENT; } //调用Map的clear方法清空所有Entry,也就清空了HashSet中所有元素 publicvoidclear() { map.clear(); } ... }
由上面源程序可以看出,HashSet的实现其实非常简单,它只是封装了一个HashMap对象来存储所有的集合元素,所有放入HashSet中的集合元素实际上由HashMap的key来保存,而HashMap的value则存储了一个PRESENT,它是一个静态的Object对象。
HashSet的绝大部分方法都是通过调用HashMap的方法来实现的,因此HashSet和HashMap两个集合在实现本质上是相同的。
HashMap的put与HashSet的add
由于HashSet的add()方法添加集合元素时实际上转变为调用HashMap的put()方法来添加key-value对,当新放入HashMap的Entry中key与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),新添加的Entry的value将覆盖原来Entry的value,但key不会有任何改变,因此如果向HashSet中添加一个已经存在的元素,新添加的集合元素(底层由HashMap的key保存)不会覆盖已有的集合元素。
掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了HashMap和HashSet集合的功能。
主要说明的就是重写equals()方法时,就必须重写hashCode()方法。
className { privateStringfirst; privateStringlast; publicName(Stringfirst,Stringlast) { this.first=first; this.last=last; } publicbooleanequals(Objecto) { if(this==o) { returntrue; } if(o.getClass()==Name.class) { Namen=(Name)o; returnn.first.equals(first) &&n.last.equals(last); } returnfalse; } } publicclassHashSetTest { publicstaticvoidmain(String[]args) { Sets=newHashSet (); s.add(newName("abc","123")); System.out.println( s.contains(newName("abc","123"))); } }
上面程序中向HashSet里添加了一个newName("abc","123")对象之后,立即通过程序判断该HashSet是否包含一个newName("abc","123")对象。粗看上去,很容易以为该程序会输出true。
实际运行上面程序将看到程序输出false,这是因为HashSet判断两个对象相等的标准除了要求通过equals()方法比较返回true之外,还要求两个对象的hashCode()返回值相等。而上面程序没有重写Name类的hashCode()方法,两个Name对象的hashCode()返回值并不相同,因此HashSet会把它们当成2个对象处理,因此程序返回false。
由此可见,当我们试图把某个类的对象当成HashMap的key,或试图将这个类的对象放入HashSet中保存时,重写该类的equals(Objectobj)方法和hashCode()方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的hashCode()返回值相同时,它们通过equals()方法比较也应该返回true。通常来说,所有参与计算hashCode()返回值的关键属性,都应该用于作为equals()比较的标准。
如下程序就正确重写了Name类的hashCode()和equals()方法,程序如下:
className { privateStringfirst; privateStringlast; publicName(Stringfirst,Stringlast) { this.first=first; this.last=last; } //根据first判断两个Name是否相等 publicbooleanequals(Objecto) { if(this==o) { returntrue; } if(o.getClass()==Name.class) { Namen=(Name)o; returnn.first.equals(first); } returnfalse; } //根据first计算Name对象的hashCode()返回值 publicinthashCode() { returnfirst.hashCode(); } publicStringtoString() { return"Name[first="+first+",last="+last+"]"; } } publicclassHashSetTest2 { publicstaticvoidmain(String[]args) { HashSetset=newHashSet (); set.add(newName("abc","123")); set.add(newName("abc","456")); System.out.println(set); } }
上面程序中提供了一个Name类,该Name类重写了equals()和toString()两个方法,这两个方法都是根据Name类的first实例变量来判断的,当两个Name对象的first实例变量相等时,这两个Name对象的hashCode()返回值也相同,通过equals()比较也会返回true。
程序主方法先将第一个Name对象添加到HashSet中,该Name对象的first实例变量值为"abc",接着程序再次试图将一个first为"abc"的Name对象添加到HashSet中,很明显,此时没法将新的Name对象添加到该HashSet中,因为此处试图添加的Name对象的first也是"abc",HashSet会判断此处新增的Name对象与原有的Name对象相同,因此无法添加进入,程序在①号代码处输出set集合时将看到该集合里只包含一个Name对象,就是第一个、last为"123"的Name对象。
以上所述是小编给大家介绍的HashSet工作原理_动力节点Java学院整理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!