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 对

象。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/industrynews/13805.html

(0)
上一篇 2021年7月19日 16:12
下一篇 2021年7月19日 16:12

相关推荐

发表回复

登录后才能评论