转自:http://blog.csdn.net/wuwenxiang91322/article/details/7635123
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); ... public HashSet() { map = new HashMap<E,Object>(); } 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); } public Iterator<E> iterator() { return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } public boolean add(E e) { return map.put(e, PRESENT) == null ; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } ... }
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较 由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。 HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同 的。 掌握上面理论知识之后,接下来看一个示例程序,测试 一下自己是否真正掌握了 HashMap 和 HashSet 集合的功能。 Java 代码
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 里添加了一个 new Name("abc", "123") 对象之后,立即通过程序判断该 HashSet 是否包含一个
new Name("abc", "123") 对象。粗看上去,很容易以为该程序会输出 true。
实际运行上面程序将看到程序输出 false,这是因为 HashSet 判断两个对象相等的标准除了要求通过 equals() 方法比较返
回 true 之外,还要求两个对象的 hashCode() 返回值相等。而上面程序没有重写 Name 类的 hashCode() 方法,两个 Name
对象的 hashCode() 返回值并不相同,因此 HashSet 会把它们当成 2 个对象处理,因此程序返回 false。
由此可见,当我们试图把某个类的对象当成 HashMap 的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的
equals(Object obj) 方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode
() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属
性,都应该用于作为 equals() 比较的标准。
如下程序就正确重写了 Name 类的 hashCode() 和 equals() 方法,程序如下:
Java代码
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); } return false ; } 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); } }
上面程序中提供了一个 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 对
象。