Java中的HashSet详解和使用示例_动力节点Java学院整理
第1部分 hashset介绍
hashset 简介
hashset 是一个没有重复元素的集合。
它是由hashmap实现的,不保证元素的顺序,而且hashset允许使用 null 元素。
hashset是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 collections.synchronizedset 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
set s = collections.synchronizedset(new hashset(...));
hashset通过iterator()返回的迭代器是fail-fast的。
hashset的构造函数
// 默认构造函数 public hashset() // 带集合的构造函数 public hashset(collection<? extends e> c) // 指定hashset初始容量和加载因子的构造函数 public hashset(int initialcapacity, float loadfactor) // 指定hashset初始容量的构造函数 public hashset(int initialcapacity) // 指定hashset初始容量和加载因子的构造函数,dummy没有任何作用 hashset(int initialcapacity, float loadfactor, boolean dummy)
hashset的主要api
boolean add(e object) void clear() object clone() boolean contains(object object) boolean isempty() iterator<e> iterator() boolean remove(object object) int size()
第2部分 hashset数据结构
hashset的继承关系如下:
java.lang.object java.util.abstractcollection<e> java.util.abstractset<e> java.util.hashset<e> public class hashset<e> extends abstractset<e> implements set<e>, cloneable, java.io.serializable { }
hashset与map关系如下图:
从图中可以看出:
(01) hashset继承于abstractset,并且实现了set接口。
(02) hashset的本质是一个"没有重复元素"的集合,它是通过hashmap实现的。hashset中含有一个"hashmap类型的成员变量"map,hashset的操作函数,实际上都是通过map实现的。
第3部分 hashset源码解析(基于jdk1.6.0_45)
为了更了解hashset的原理,下面对hashset源码代码作出分析。
package java.util; public class hashset<e> extends abstractset<e> implements set<e>, cloneable, java.io.serializable { static final long serialversionuid = -5024744406713321676l; // hashset是通过map(hashmap对象)保存内容的 private transient hashmap<e,object> map; // present是向map中插入key-value对应的value // 因为hashset中只需要用到key,而hashmap是key-value键值对; // 所以,向map中添加键值对时,键值对的值固定是present private static final object present = new object(); // 默认构造函数 public hashset() { // 调用hashmap的默认构造函数,创建map map = new hashmap<e,object>(); } // 带集合的构造函数 public hashset(collection<? extends e> c) { // 创建map。 // 为什么要调用math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢? // 首先,说明(c.size()/.75f) + 1 // 因为从hashmap的效率(时间成本和空间成本)考虑,hashmap的加载因子是0.75。 // 当hashmap的“阈值”(阈值=hashmap总的大小*加载因子) < “hashmap实际大小”时, // 就需要将hashmap的容量翻倍。 // 所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。 // 接下来,说明为什么是 16 。 // hashmap的总的大小,必须是2的指数倍。若创建hashmap时,指定的大小不是2的指数倍; // hashmap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。 // 所以,这里指定为16是从性能考虑。避免重复计算。 map = new hashmap<e,object>(math.max((int) (c.size()/.75f) + 1, 16)); // 将集合(c)中的全部元素添加到hashset中 addall(c); } // 指定hashset初始容量和加载因子的构造函数 public hashset(int initialcapacity, float loadfactor) { map = new hashmap<e,object>(initialcapacity, loadfactor); } // 指定hashset初始容量的构造函数 public hashset(int initialcapacity) { map = new hashmap<e,object>(initialcapacity); } hashset(int initialcapacity, float loadfactor, boolean dummy) { map = new linkedhashmap<e,object>(initialcapacity, loadfactor); } // 返回hashset的迭代器 public iterator<e> iterator() { // 实际上返回的是hashmap的“key集合的迭代器” return map.keyset().iterator(); } public int size() { return map.size(); } public boolean isempty() { return map.isempty(); } public boolean contains(object o) { return map.containskey(o); } // 将元素(e)添加到hashset中 public boolean add(e e) { return map.put(e, present)==null; } // 删除hashset中的元素(o) public boolean remove(object o) { return map.remove(o)==present; } public void clear() { map.clear(); } // 克隆一个hashset,并返回object对象 public object clone() { try { hashset<e> newset = (hashset<e>) super.clone(); newset.map = (hashmap<e, object>) map.clone(); return newset; } catch (clonenotsupportedexception e) { throw new internalerror(); } } // java.io.serializable的写入函数 // 将hashset的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中 private void writeobject(java.io.objectoutputstream s) throws java.io.ioexception { // write out any hidden serialization magic s.defaultwriteobject(); // write out hashmap capacity and load factor s.writeint(map.capacity()); s.writefloat(map.loadfactor()); // write out size s.writeint(map.size()); // write out all elements in the proper order. for (iterator i=map.keyset().iterator(); i.hasnext(); ) s.writeobject(i.next()); } // java.io.serializable的读取函数 // 将hashset的“总的容量,加载因子,实际容量,所有的元素”依次读出 private void readobject(java.io.objectinputstream s) throws java.io.ioexception, classnotfoundexception { // read in any hidden serialization magic s.defaultreadobject(); // read in hashmap capacity and load factor and create backing hashmap int capacity = s.readint(); float loadfactor = s.readfloat(); map = (((hashset)this) instanceof linkedhashset ? new linkedhashmap<e,object>(capacity, loadfactor) : new hashmap<e,object>(capacity, loadfactor)); // read in size int size = s.readint(); // read in all elements in the proper order. for (int i=; i<size; i++) { e e = (e) s.readobject(); map.put(e, present); } } }
说明: hashset的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过hashmap实现的,若对hashset的理解有困难,建议先学习以下hashmap;学完hashmap之后,在学习hashset就非常容易了。
第4部分 hashset遍历方式
4.1 通过iterator遍历hashset
第一步:根据iterator()获取hashset的迭代器。
第二步:遍历迭代器获取各个元素。
// 假设set是hashset对象 for(iterator iterator = set.iterator(); iterator.hasnext(); ) { iterator.next(); }
4.2 通过for-each遍历hashset
第一步:根据toarray()获取hashset的元素集合对应的数组。
第二步:遍历数组,获取各个元素。
// 假设set是hashset对象,并且set中元素是string类型 string[] arr = (string[])set.toarray(new string[0]); for (string str:arr) system.out.printf("for each : %s\n", str);
hashset的遍历测试程序如下:
import java.util.random; import java.util.iterator; import java.util.hashset; /* * @desc 介绍hashset遍历方法 * * */ public class hashsetiteratortest { public static void main(string[] args) { // 新建hashset hashset set = new hashset(); // 添加元素 到hashset中 for (int i=; i<; i++) set.add(""+i); // 通过iterator遍历hashset iteratorhashset(set) ; // 通过for-each遍历hashset foreachhashset(set); } /* * 通过iterator遍历hashset。推荐方式 */ private static void iteratorhashset(hashset set) { for(iterator iterator = set.iterator(); iterator.hasnext(); ) { system.out.printf("iterator : %s\n", iterator.next()); } } /* * 通过for-each遍历hashset。不推荐!此方法需要先将set转换为数组 */ private static void foreachhashset(hashset set) { string[] arr = (string[])set.toarray(new string[]); for (string str:arr) system.out.printf("for each : %s\n", str); } }
运行结果:
iterator : 3 iterator : 2 iterator : 1 iterator : 0 iterator : 4 for each : 3 for each : 2 for each : 1 for each : 0 for each : 4
第5部分 hashset示例
下面我们通过实例学习如何使用hashset
import java.util.iterator; import java.util.hashset; /* * @desc hashset常用api的使用。 * * @author skywang */ public class hashsettest { public static void main(string[] args) { // hashset常用api testhashsetapis() ; } /* * hashset除了iterator()和add()之外的其它常用api */ private static void testhashsetapis() { // 新建hashset hashset set = new hashset(); // 将元素添加到set中 set.add("a"); set.add("b"); set.add("c"); set.add("d"); set.add("e"); // 打印hashset的实际大小 system.out.printf("size : %d\n", set.size()); // 判断hashset是否包含某个值 system.out.printf("hashset contains a :%s\n", set.contains("a")); system.out.printf("hashset contains g :%s\n", set.contains("g")); // 删除hashset中的“e” set.remove("e"); // 将set转换为数组 string[] arr = (string[])set.toarray(new string[]); for (string str:arr) system.out.printf("for each : %s\n", str); // 新建一个包含b、c、f的hashset hashset otherset = new hashset(); otherset.add("b"); otherset.add("c"); otherset.add("f"); // 克隆一个removeset,内容和set一模一样 hashset removeset = (hashset)set.clone(); // 删除“removeset中,属于otherset的元素” removeset.removeall(otherset); // 打印removeset system.out.printf("removeset : %s\n", removeset); // 克隆一个retainset,内容和set一模一样 hashset retainset = (hashset)set.clone(); // 保留“retainset中,属于otherset的元素” retainset.retainall(otherset); // 打印retainset system.out.printf("retainset : %s\n", retainset); // 遍历hashset for(iterator iterator = set.iterator(); iterator.hasnext(); ) system.out.printf("iterator : %s\n", iterator.next()); // 清空hashset set.clear(); // 输出hashset是否为空 system.out.printf("%s\n", set.isempty()?"set is empty":"set is not empty"); } }
运行结果:
size : 5 hashset contains a :true hashset contains g :false for each : d for each : b for each : c for each : a removeset : [d, a] retainset : [b, c] iterator : d iterator : b iterator : c iterator : a set is empty
以上所述是小编给大家介绍的java中的hashset详解和使用示例_动力节点java学院整理,希望对大家有所帮助
推荐阅读
-
Java中的HashSet详解和使用示例_动力节点Java学院整理
-
Java中的PrintWriter 介绍_动力节点Java学院整理
-
Java 中的Printstream介绍_动力节点Java学院整理
-
Java中LinkedList详解和使用示例_动力节点Java学院整理
-
详解PipedInputStream和PipedOutputStream_动力节点Java学院整理
-
TreeSet详解和使用示例_动力节点Java学院整理
-
Java Thread中start()和run()的区别_动力节点Java学院整理
-
Java中实现线程的三种方式及对比_动力节点Java学院整理
-
File的API和常用方法详解_动力节点Java学院整理
-
Java 中的CharArrayReader 介绍_动力节点Java学院整理