专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 Java基础内容分享,hashset取值详解

Java基础内容分享,hashset取值详解

更新时间:2020-05-27 15:47:41 来源:动力节点 浏览7234次

对于HashSet而言,它是基于HashMap实现的,HashSet底层采用HashMap来保存所有元素,因此HashSet的实现比较简单,查看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的key来保存,而HashMap的value则存储了一个PRESENT,它是一个静态的Object对象。

HashSet的绝大部分方法都是通过调用HashMap的方法来实现的,因此HashSet和HashMap两个集合在实现本质上是相同的。

掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了HashMap和HashSet集合的功能。

 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()方法,程序如下:

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对象。

Java基础内容分享,hashset取值详解

以上就是动力节点java培训机构的小编针对“Java基础内容分享,hashset取值详解”的内容进行的回答,希望对大家有所帮助,如有疑问,请在线咨询,有专业老师随时为你服务。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>