java中HashSet详解
HashSet 的实现
对于 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中HashSet详解(转)
HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...
- java集合(4)- java中HashSet详解
HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...
- 【转】 java中HashMap详解
原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...
- java中HashMap详解(转)
java中HashMap详解 博客分类: JavaSE Java算法JDK编程生活 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成 ...
- java集合(2)- java中HashMap详解
java中HashMap详解 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 H ...
- java中多线程详解-synchronized
一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...
- JAVA 中 synchronized 详解
看到一篇关于JAVA中synchronized的用法的详解,觉得不错遂转载之..... 原文地址: http://www.cnblogs.com/GnagWang/archive/2011/02/27 ...
- Java 中HashMap 详解
本篇重点: 1.HashMap的存储结构 2.HashMap的put和get操作过程 3.HashMap的扩容 4.关于transient关键字 HashMap的存储结构 1. HashMap 总体是 ...
- Java中List详解
List是Java中比较常用的集合类,关于List接口有很多实现类,本文就来简单介绍下其中几个重点的实现ArrayList.LinkedList和Vector之间的关系和区别. List List 是 ...
随机推荐
- ajaxFileUpload+struts2多文件上传(动态添加文件上传框)
上一篇文章http://blog.csdn.net/itmyhome1990/article/details/36396291介绍了ajaxfileupload实现多文件上传, 但仅仅是固定的文件个数 ...
- Tomcat通过配置一个虚拟路径管理web工程
关于虚拟路径.学问javaweb训练课程,如今,鉴于这种情况下老师. 当我们的项目,当在不同的文件夹项目.我们如何使用tomcat去管理web工程. 教师提出的解决方案是 使用虚拟路径方式,并按照实施 ...
- SQL SERVER SQLOS的任务调度
原文:SQL SERVER SQLOS的任务调度 原文地址:http://blogs.msdn.com/b/apgcdsd/archive/2011/11/24/sql-server-sqlos.as ...
- 恶意软件"跨平台" 小心钱包很受伤
什么是跨平台攻击? 举例来说.就像网络诈骗犯为了避开电子商务平台的监控.会在微博上发消息.百度上撒网,腾讯上联系,最后在淘宝上交易.这样的跨平台操作的模式会大大添加犯罪过程监控和取证的难度.而跨平台攻 ...
- W3C DOM 事件模型(简述)
1.事件模型 由于事件捕获与冒泡模型都有其长处和解释,DOM标准支持捕获型与冒泡型,能够说是它们两者的结合体.它能够在一个DOM元素上绑定多个事件处理器,而且在处理函数内部,thiskeyword仍然 ...
- [Java Web]Struts2加起来(一个)
Struts2环境配置 进口Struts2的需要jar包 在WEB-INF/classes(src)文件夹下创建struts.xml文件 在web.xml文件里加入Struts过滤器信息 经常使用配置 ...
- iOS如何兼容的应用程序32位系统和64Bit系统
苹果发布iPhone5S时刻,64应用程序位去了眼前.当时我看到苹果公布的官方数据iOS7.x的SDK支撑64位应用程序.而内置的应用程序已经64位置. 我记得自己刚刚接触电脑时还有16位的系统,指针 ...
- debian软件安装基础(同tomcat案件)
基本介绍 笔者是一个Linux盲.一旦只在虚拟机上载通过Ubantu-图形版本,我看着接口.打了几场比赛卸载的光盘上. 往下看,在过去的几天.试想想,在Linux关于建设nexus(mavenPW)玩 ...
- 09应用输入经理旋转场景--《猿学校课程Unity3d》
为什么极品飞车游戏等.,我们可以通过系统设置非常的方面根据自己喜欢的操作模式设置,有些人喜欢用箭头来控制不喜欢与使用"W,S,A,D"控制,这就解释程序猿不会死在程序写入内部控制, ...
- 国外流行的共享网站实现:facebook,twitter,google+1,tumblr等待
近期需要做相关的国外几个站点共享,本来我以为它会和weibo.在同样的烦恼空间,什么appkey啦,apptoken啦.api啦.结果非常意外的发现并非如此恼火. Twitter分享: https:/ ...