当前位置: 移动技术网 > IT编程>开发语言>Java > Java中的HashSet详解和使用示例_动力节点Java学院整理

Java中的HashSet详解和使用示例_动力节点Java学院整理

2019年07月22日  | 移动技术网IT编程  | 我要评论

第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学院整理,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网