java中HashSet详解

public class HashSet<E>   
 extends AbstractSet<E>   
 implements Set<E>, Cloneable, java.io.Serializable   
{   
 // 使用 HashMap 的 key 保存 HashSet 中所有元素  
 private transient HashMap<E,Object> map;   
 // 定义一个虚拟的 Object 对象作为 HashMap 的 value   
 private static final Object PRESENT = new Object();   
 ...   
 // 初始化 HashSet,底层会初始化一个 HashMap   
 public HashSet()   
 {   
     map = new HashMap<E,Object>();   
 }   
 // 以指定的 initialCapacity、loadFactor 创建 HashSet   
 // 其实就是以相应的参数创建 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);   
 }   
 // 调用 map 的 keySet 来返回所有的 key   
 public Iterator<E> iterator()   
 {   
     return map.keySet().iterator();   
 }   
 // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数  
 public int size()   
 {   
     return map.size();   
 }   
 // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,  
 // 当 HashMap 为空时,对应的 HashSet 也为空  
 public boolean isEmpty()   
 {   
     return map.isEmpty();   
 }   
 // 调用 HashMap 的 containsKey 判断是否包含指定 key   
 //HashSet 的所有元素就是通过 HashMap 的 key 来保存的  
 public boolean contains(Object o)   
 {   
     return map.containsKey(o);   
 }   
 // 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap   
 public boolean add(E e)   
 {   
     return map.put(e, PRESENT) == null;   
 }   
 // 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素  
 public boolean remove(Object o)   
 {   
     return map.remove(o)==PRESENT;   
 }   
 // 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素  
 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;   
    }   
    // 根据 first 判断两个 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;   
    }   
       
    // 根据 first 计算 Name 对象的 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);   
    }   
}  


上面程序中提供了一个 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 对
象。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值