java中HashSet详解

来源:互联网 发布:剑灵男灵剑士捏脸数据 编辑:程序博客网 时间:2024/05/16 19:27
转自:http://blog.csdn.net/wuwenxiang91322/article/details/7635123
[java] view plain copy
  1. public class HashSet<E>     
  2.  extends AbstractSet<E>     
  3.  implements Set<E>, Cloneable, java.io.Serializable     
  4. {     
  5.  // 使用 HashMap 的 key 保存 HashSet 中所有元素    
  6.  private transient HashMap<E,Object> map;     
  7.  // 定义一个虚拟的 Object 对象作为 HashMap 的 value     
  8.  private static final Object PRESENT = new Object();     
  9.  ...     
  10.  // 初始化 HashSet,底层会初始化一个 HashMap     
  11.  public HashSet()     
  12.  {     
  13.      map = new HashMap<E,Object>();     
  14.  }     
  15.  // 以指定的 initialCapacity、loadFactor 创建 HashSet     
  16.  // 其实就是以相应的参数创建 HashMap     
  17.  public HashSet(int initialCapacity, float loadFactor)     
  18.  {     
  19.      map = new HashMap<E,Object>(initialCapacity, loadFactor);     
  20.  }     
  21.  public HashSet(int initialCapacity)     
  22.  {     
  23.      map = new HashMap<E,Object>(initialCapacity);     
  24.  }     
  25.  HashSet(int initialCapacity, float loadFactor, boolean dummy)     
  26.  {     
  27.      map = new LinkedHashMap<E,Object>(initialCapacity     
  28.          , loadFactor);     
  29.  }     
  30.  // 调用 map 的 keySet 来返回所有的 key     
  31.  public Iterator<E> iterator()     
  32.  {     
  33.      return map.keySet().iterator();     
  34.  }     
  35.  // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数    
  36.  public int size()     
  37.  {     
  38.      return map.size();     
  39.  }     
  40.  // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,    
  41.  // 当 HashMap 为空时,对应的 HashSet 也为空    
  42.  public boolean isEmpty()     
  43.  {     
  44.      return map.isEmpty();     
  45.  }     
  46.  // 调用 HashMap 的 containsKey 判断是否包含指定 key     
  47.  //HashSet 的所有元素就是通过 HashMap 的 key 来保存的    
  48.  public boolean contains(Object o)     
  49.  {     
  50.      return map.containsKey(o);     
  51.  }     
  52.  // 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap     
  53.  public boolean add(E e)     
  54.  {     
  55.      return map.put(e, PRESENT) == null;     
  56.  }     
  57.  // 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素    
  58.  public boolean remove(Object o)     
  59.  {     
  60.      return map.remove(o)==PRESENT;     
  61.  }     
  62.  // 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素    
  63.  public void clear()     
  64.  {     
  65.      map.clear();     
  66.  }     
  67.  ...     
  68. }     



对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较
由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 
HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 
Object 对象。 


HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同
的。 
掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了 HashMap 和 HashSet 集合的功能。 
Java代码

[java] view plain copy
  1.  class Name    
  2. {    
  3.     private String first;     
  4.     private String last;     
  5.         
  6.     public Name(String first, String last)     
  7.     {     
  8.         this.first = first;     
  9.         this.last = last;     
  10.     }     
  11.     
  12.     public boolean equals(Object o)     
  13.     {     
  14.         if (this == o)     
  15.         {     
  16.             return true;     
  17.         }     
  18.             
  19.     if (o.getClass() == Name.class)     
  20.         {     
  21.             Name n = (Name)o;     
  22.             return n.first.equals(first)     
  23.                 && n.last.equals(last);     
  24.         }     
  25.         return false;     
  26.     }     
  27. }    
  28.     
  29. public class HashSetTest    
  30. {    
  31.     public static void main(String[] args)    
  32.     {     
  33.         Set<Name> s = new HashSet<Name>();    
  34.         s.add(new Name("abc""123"));    
  35.         System.out.println(    
  36.             s.contains(new Name("abc""123")));    
  37.     }    
  38. }     

上面程序中向 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代码 

[java] view plain copy
  1. class Name     
  2. {     
  3.     private String first;    
  4.     private String last;    
  5.     public Name(String first, String last)    
  6.     {     
  7.         this.first = first;     
  8.         this.last = last;     
  9.     }     
  10.     // 根据 first 判断两个 Name 是否相等    
  11.     public boolean equals(Object o)     
  12.     {     
  13.         if (this == o)     
  14.         {     
  15.             return true;     
  16.         }     
  17.         if (o.getClass() == Name.class)     
  18.         {     
  19.             Name n = (Name)o;     
  20.             return n.first.equals(first);     
  21.         }     
  22.         return false;     
  23.     }     
  24.          
  25.     // 根据 first 计算 Name 对象的 hashCode() 返回值    
  26.     public int hashCode()     
  27.     {     
  28.         return first.hashCode();     
  29.     }    
  30.     
  31.     public String toString()     
  32.     {     
  33.         return "Name[first=" + first + ", last=" + last + "]";     
  34.     }     
  35.  }     
  36.      
  37.  public class HashSetTest2     
  38.  {     
  39.     public static void main(String[] args)     
  40.     {     
  41.         HashSet<Name> set = new HashSet<Name>();     
  42.         set.add(new Name("abc" , "123"));     
  43.         set.add(new Name("abc" , "456"));     
  44.         System.out.println(set);     
  45.     }     
  46. }    


上面程序中提供了一个 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 对
象。
原创粉丝点击