1,HashSet介绍

1)HashSet 是一个没有重复元素的集合
2)它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
3)HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

  1. Set s = Collections.synchronizedSet(new HashSet(...));

4)HashSet通过iterator()返回的迭代器是fail-fast的。

HashSet的构造函数

  1. // 默认构造函数
  2. public HashSet()
  3.  
  4. // 带集合的构造函数
  5. public HashSet(Collection<? extends E> c)
  6.  
  7. // 指定HashSet初始容量和加载因子的构造函数
  8. public HashSet(int initialCapacity, float loadFactor)
  9.  
  10. // 指定HashSet初始容量的构造函数
  11. public HashSet(int initialCapacity)
  12.  
  13. // 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用
  14. HashSet(int initialCapacity, float loadFactor, boolean dummy)

HashSet的主要API

  1. boolean add(E object)
  2. void clear()
  3. Object clone()
  4. boolean contains(Object object)
  5. boolean isEmpty()
  6. Iterator<E> iterator()
  7. boolean remove(Object object)
  8. int size()

2,HashSet数据结构

HashSet的继承关系如下:

  1. java.lang.Object
  2. java.util.AbstractCollection<E>
  3. java.util.AbstractSet<E>
  4. java.util.HashSet<E>
  5.  
  6. public class HashSet<E>
  7. extends AbstractSet<E>
  8. implements Set<E>, Cloneable, java.io.Serializable { }

HashSet与Map关系如下图:

从图中可以看出:
(01) HashSet继承于AbstractSet,并且实现了Set接口。
(02) HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,HashSet的操作函数,实际上都是通过map实现的。

3,HashSet源码解析

为了更了解HashSet的原理,下面对HashSet源码代码作出分析。

  1. package java.util;
  2.  
  3. public class HashSet<E>
  4. extends AbstractSet<E>
  5. implements Set<E>, Cloneable, java.io.Serializable
  6. {
  7. static final long serialVersionUID = -5024744406713321676L;
  8.  
  9. // HashSet是通过map(HashMap对象)保存内容的
  10. private transient HashMap<E,Object> map;
  11.  
  12. // PRESENT是向map中插入key-value对应的value
  13. // 因为HashSet中只需要用到key,而HashMap是key-value键值对;
  14. // 所以,向map中添加键值对时,键值对的值固定是PRESENT
  15. private static final Object PRESENT = new Object();
  16.  
  17. // 默认构造函数
  18. public HashSet() {
  19. // 调用HashMap的默认构造函数,创建map
  20. map = new HashMap<E,Object>();
  21. }
  22.  
  23. // 带集合的构造函数
  24. public HashSet(Collection<? extends E> c) {
  25. // 创建map。
  26. // 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢?
  27. // 首先,说明(c.size()/.75f) + 1
  28. // 因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。
  29. // 当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,
  30. // 就需要将HashMap的容量翻倍。
  31. // 所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。
  32. // 接下来,说明为什么是 16 。
  33. // HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍;
  34. // HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。
  35. // 所以,这里指定为16是从性能考虑。避免重复计算。
  36. map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
  37. // 将集合(c)中的全部元素添加到HashSet中
  38. addAll(c);
  39. }
  40.  
  41. // 指定HashSet初始容量和加载因子的构造函数
  42. public HashSet(int initialCapacity, float loadFactor) {
  43. map = new HashMap<E,Object>(initialCapacity, loadFactor);
  44. }
  45.  
  46. // 指定HashSet初始容量的构造函数
  47. public HashSet(int initialCapacity) {
  48. map = new HashMap<E,Object>(initialCapacity);
  49. }
  50.  
  51. HashSet(int initialCapacity, float loadFactor, boolean dummy) {
  52. map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
  53. }
  54.  
  55. // 返回HashSet的迭代器
  56. public Iterator<E> iterator() {
  57. // 实际上返回的是HashMap的“key集合的迭代器”
  58. return map.keySet().iterator();
  59. }
  60.  
  61. public int size() {
  62. return map.size();
  63. }
  64.  
  65. public boolean isEmpty() {
  66. return map.isEmpty();
  67. }
  68.  
  69. public boolean contains(Object o) {
  70. return map.containsKey(o);
  71. }
  72.  
  73. // 将元素(e)添加到HashSet中
  74. public boolean add(E e) {
  75. return map.put(e, PRESENT)==null;
  76. }
  77.  
  78. // 删除HashSet中的元素(o)
  79. public boolean remove(Object o) {
  80. return map.remove(o)==PRESENT;
  81. }
  82.  
  83. public void clear() {
  84. map.clear();
  85. }
  86.  
  87. // 克隆一个HashSet,并返回Object对象
  88. public Object clone() {
  89. try {
  90. HashSet<E> newSet = (HashSet<E>) super.clone();
  91. newSet.map = (HashMap<E, Object>) map.clone();
  92. return newSet;
  93. } catch (CloneNotSupportedException e) {
  94. throw new InternalError();
  95. }
  96. }
  97.  
  98. // java.io.Serializable的写入函数
  99. // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中
  100. private void writeObject(java.io.ObjectOutputStream s)
  101. throws java.io.IOException {
  102. // Write out any hidden serialization magic
  103. s.defaultWriteObject();
  104.  
  105. // Write out HashMap capacity and load factor
  106. s.writeInt(map.capacity());
  107. s.writeFloat(map.loadFactor());
  108.  
  109. // Write out size
  110. s.writeInt(map.size());
  111.  
  112. // Write out all elements in the proper order.
  113. for (Iterator i=map.keySet().iterator(); i.hasNext(); )
  114. s.writeObject(i.next());
  115. }
  116.  
  117. // java.io.Serializable的读取函数
  118. // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出
  119. private void readObject(java.io.ObjectInputStream s)
  120. throws java.io.IOException, ClassNotFoundException {
  121. // Read in any hidden serialization magic
  122. s.defaultReadObject();
  123.  
  124. // Read in HashMap capacity and load factor and create backing HashMap
  125. int capacity = s.readInt();
  126. float loadFactor = s.readFloat();
  127. map = (((HashSet)this) instanceof LinkedHashSet ?
  128. new LinkedHashMap<E,Object>(capacity, loadFactor) :
  129. new HashMap<E,Object>(capacity, loadFactor));
  130.  
  131. // Read in size
  132. int size = s.readInt();
  133.  
  134. // Read in all elements in the proper order.
  135. for (int i=0; i<size; i++) {
  136. E e = (E) s.readObject();
  137. map.put(e, PRESENT);
  138. }
  139. }
  140. }

说明: HashSet的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过HashMap实现的,若对HashSet的理解有困难,建议先学习以下HashMap;学完HashMap之后,在学习HashSet就非常容易了。

4,HashSet遍历方式

4.1 通过Iterator遍历HashSet

第一步:根据iterator()获取HashSet的迭代器。
第二步:遍历迭代器获取各个元素

  1. // 假设set是HashSet对象
  2. for(Iterator iterator = set.iterator();
  3. iterator.hasNext(); ) {
  4. iterator.next();
  5. }

4.2 通过for-each遍历HashSet

第一步:根据toArray()获取HashSet的元素集合对应的数组。
第二步:遍历数组,获取各个元素。

  1. // 假设set是HashSet对象,并且set中元素是String类型
  2. String[] arr = (String[])set.toArray(new String[0]);
  3. for (String str:arr)
  4. System.out.printf("for each : %s\n", str);

HashSet的遍历测试程序如下:

  1. import java.util.Random;
  2. import java.util.Iterator;
  3. import java.util.HashSet;
  4.  
  5. /*
  6. * @desc 介绍HashSet遍历方法
  7. *
  8. * @author skywang
  9. */
  10. public class HashSetIteratorTest {
  11.  
  12. public static void main(String[] args) {
  13. // 新建HashSet
  14. HashSet set = new HashSet();
  15.  
  16. // 添加元素 到HashSet中
  17. for (int i=0; i<5; i++)
  18. set.add(""+i);
  19.  
  20. // 通过Iterator遍历HashSet
  21. iteratorHashSet(set) ;
  22.  
  23. // 通过for-each遍历HashSet
  24. foreachHashSet(set);
  25. }
  26.  
  27. /*
  28. * 通过Iterator遍历HashSet。推荐方式
  29. */
  30. private static void iteratorHashSet(HashSet set) {
  31. for(Iterator iterator = set.iterator();
  32. iterator.hasNext(); ) {
  33. System.out.printf("iterator : %s\n", iterator.next());
  34. }
  35. }
  36.  
  37. /*
  38. * 通过for-each遍历HashSet。不推荐!此方法需要先将Set转换为数组
  39. */
  40. private static void foreachHashSet(HashSet set) {
  41. String[] arr = (String[])set.toArray(new String[0]);
  42. for (String str:arr)
  43. System.out.printf("for each : %s\n", str);
  44. }
  45. }

运行结果

  1. iterator : 3
  2. iterator : 2
  3. iterator : 1
  4. iterator : 0
  5. iterator : 4
  6. for each : 3
  7. for each : 2
  8. for each : 1
  9. for each : 0
  10. for each : 4

5,HashSet示例

  1. import java.util.Iterator;
  2. import java.util.HashSet;
  3.  
  4. /*
  5. * @desc HashSet常用API的使用。
  6. *
  7. * @author skywang
  8. */
  9. public class HashSetTest {
  10.  
  11. public static void main(String[] args) {
  12. // HashSet常用API
  13. testHashSetAPIs() ;
  14. }
  15.  
  16. /*
  17. * HashSet除了iterator()和add()之外的其它常用API
  18. */
  19. private static void testHashSetAPIs() {
  20. // 新建HashSet
  21. HashSet set = new HashSet();
  22.  
  23. // 将元素添加到Set中
  24. set.add("a");
  25. set.add("b");
  26. set.add("c");
  27. set.add("d");
  28. set.add("e");
  29.  
  30. // 打印HashSet的实际大小
  31. System.out.printf("size : %d\n", set.size());
  32.  
  33. // 判断HashSet是否包含某个值
  34. System.out.printf("HashSet contains a :%s\n", set.contains("a"));
  35. System.out.printf("HashSet contains g :%s\n", set.contains("g"));
  36.  
  37. // 删除HashSet中的“e”
  38. set.remove("e");
  39.  
  40. // 将Set转换为数组
  41. String[] arr = (String[])set.toArray(new String[0]);
  42. for (String str:arr)
  43. System.out.printf("for each : %s\n", str);
  44.  
  45. // 新建一个包含b、c、f的HashSet
  46. HashSet otherset = new HashSet();
  47. otherset.add("b");
  48. otherset.add("c");
  49. otherset.add("f");
  50.  
  51. // 克隆一个removeset,内容和set一模一样
  52. HashSet removeset = (HashSet)set.clone();
  53. // 删除“removeset中,属于otherSet的元素”
  54. removeset.removeAll(otherset);
  55. // 打印removeset
  56. System.out.printf("removeset : %s\n", removeset);
  57.  
  58. // 克隆一个retainset,内容和set一模一样
  59. HashSet retainset = (HashSet)set.clone();
  60. // 保留“retainset中,属于otherSet的元素”
  61. retainset.retainAll(otherset);
  62. // 打印retainset
  63. System.out.printf("retainset : %s\n", retainset);
  64.  
  65. // 遍历HashSet
  66. for(Iterator iterator = set.iterator();
  67. iterator.hasNext(); )
  68. System.out.printf("iterator : %s\n", iterator.next());
  69.  
  70. // 清空HashSet
  71. set.clear();
  72.  
  73. // 输出HashSet是否为空
  74. System.out.printf("%s\n", set.isEmpty()?"set is empty":"set is not empty");
  75. }
  76.  
  77. }

运行结果

  1. size : 5
  2. HashSet contains a :true
  3. HashSet contains g :false
  4. for each : d
  5. for each : b
  6. for each : c
  7. for each : a
  8. removeset : [d, a]
  9. retainset : [b, c]
  10. iterator : d
  11. iterator : b
  12. iterator : c
  13. iterator : a
  14. set is empty

转自:http://www.cnblogs.com/skywang12345/p/3311252.html

java源码之HashSet的更多相关文章

  1. java源码阅读HashSet

    1类签名与注解 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneabl ...

  2. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

  3. 【java集合框架源码剖析系列】java源码剖析之TreeSet

    本博客将从源码的角度带领大家学习TreeSet相关的知识. 一TreeSet类的定义: public class TreeSet<E> extends AbstractSet<E&g ...

  4. jdk1.8.0_45源码解读——HashSet的实现

    jdk1.8.0_45源码解读——HashSet的实现 一.HashSet概述 HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.主要具有以下的特点: 不保证set的迭代顺 ...

  5. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  6. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  7. 如何阅读Java源码

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...

  8. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

  9. Programming a Spider in Java 源码帖

    Programming a Spider in Java 源码帖 Listing 1: Finding the bad links (CheckLinks.java) import java.awt. ...

随机推荐

  1. ASP.NET学习笔记01

    ASP.NET初级工程师的核心要求:能够实现一个基本的网站. ASP.NET初级工程师面试主要要求: 1.基础的数据结构和算法 2.C#编程语言基础 3.网站基础(HTML,CSS,Javascrip ...

  2. [SharePoint][SharePoint Designer 入门经典]Chapter13 客户端JavaScript编程

    1.创建客户对象模型的页面 2.使用CAML从SPS中取得数据 3.创建更新删除列表项目 4.为ribbon添加项目

  3. C++ 嵌入汇编程序提高计算效率

    因为汇编语言比C++更接近硬件底层,所以在性能要求高的程序中往往能够採取在C++代码中嵌入汇编的方式来给程序提速. 在VC中能够简单的通过 __asm { //在这里加入汇编代码 } 来实现. 以下通 ...

  4. 交叉编译faac共享库

    作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 Advanced Audio Coding.一种专为声音数据设计的文件压缩格式,与Mp3不同,它採 ...

  5. 关于static的使用

    在我们写类写方法的时候,通常会看到有的时候是静态的方法,有的则是动态的,那么问题来了,什么时候该加static什么时候不加static?这里的区别有多大那?那么加不加static取决与这个方法的特征与 ...

  6. 在 Eclipse 中使用 C++

    安装 安装Eclipse Eclipse下载页 能够选择Eclipse IDE for C/C++ Developers(内置CDT插件) 也能够选择安装其它版本号之后再安装CDT插件. 安装CDT插 ...

  7. 从乐视和小米“最火电视”之战 看PR传播策略

        今年的双11够热闹.一方面,阿里.京东.国美.苏宁等电商巨头卯足了劲儿.试图在双11期间斗个你死我活,剑拔弩张的气势超过了以往不论什么一场双11:还有一方面.不少硬件厂商.家电企业也来凑双11 ...

  8. swift-判断是否已获得相机、相册权限

    // 相机权限 func isRightCamera() -> Bool { let authStatus = AVCaptureDevice.authorizationStatus(forMe ...

  9. DatabaseMetaData开发实务

    1.总论 在企业开发实务中,数据迁移是经常会遇到的事情,此时,需要搞清楚,源数据库与目的数据库之间表以及表内部各列之间的异同.而有些时候,我们拿到的项目文 档,未必能准确表述各个表的准确结构,即使应用 ...

  10. C#中网络通信

    一.服务端代码 using System; using System.Collections.Generic; using System.Linq; using System.Net; using S ...