Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 390|回复: 0

[集合学习]学习java中HashSet

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-29 23:56:15 | 显示全部楼层 |阅读模式
    对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:
       

       

         
          
          
           java代码
          
          
          
          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();   
           }   
           ...   
          }   
          
         
       
    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 的 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")));   
              }   
          }   
          
         
       
    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.     public boolean equals(Object o)
    12.     {
    13.         if (this == o)
    14.         {
    15.             return true;
    16.         }
    17.         
    18.         if (o.getClass() == Name.class)
    19.         {
    20.             Name n = (Name)o;
    21.             return n.first.equals(first)
    22.                 && n.last.equals(last);
    23.         }
    24.         return false;
    25.     }
    26. }
    27. public class HashSetTest
    28. {
    29.     public static void main(String[] args)
    30.     {
    31.         Set<Name> s = new HashSet<Name>();
    32.         s.add(new Name("abc", "123"));
    33.         System.out.println(
    34.             s.contains(new Name("abc", "123")));
    35.     }
    36. }
    复制代码

       
        上面程序中向 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);   
              }   
          }   
          
         
       
    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.     public String toString()
    31.     {
    32.         return "Name[first=" + first + ", last=" + last + "]";
    33.     }
    34. }

    35. public class HashSetTest2
    36. {
    37.     public static void main(String[] args)
    38.     {
    39.         HashSet<Name> set = new HashSet<Name>();
    40.         set.add(new Name("abc" , "123"));
    41.         set.add(new Name("abc" , "456"));
    42.         System.out.println(set);
    43.     }
    44. }
    复制代码
       上面程序中提供了一个 Name 类,该 Name 类重写了 equals() 和 hashCode() 两个方法,这两个方法都是根据 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 对象相同,因此无法添加进入,程序在44代码处输出 set 集合时将看到该集合里只包含一个 Name 对象,就是第一个、last 为"123"的 Name 对象。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-26 01:18 , Processed in 0.301604 second(s), 36 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表