集合框架之 HashSet

HashSet 是一个没有重复元素的集合。

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

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

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

HashSet的构造函数

1234567891011121314
public () 

// 带集合的构造函数public (Collection<? extends E> c) 

// 指定HashSet初始容量和加载因子的构造函数public (int initialCapacity, float loadFactor) 

// 指定HashSet初始容量的构造函数public (int initialCapacity) 

// 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用(int initialCapacity, float loadFactor, boolean dummy)

HashSet的主要API

12345678
boolean         add(E object)void            clear()Object          clone()boolean         contains(Object object)boolean         isEmpty()Iterator<E>     iterator()boolean         remove(Object object)int             size()

HashSet数据结构

HashSet的继承关系如下:

12345678
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关系图:

从图中可以看出:

  1. HashSet继承于AbstractSet,并且实现了Set接口。
  2. HashSet的本质是一个”没有重复元素”的集合,它是通过HashMap实现的。HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操作函数,实际上都是通过map实现的。

HashSet源码解析(基于JDK1.6.0_45)

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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
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=0; i<size; i++) {            E e = (E) s.readObject();            map.put(e, PRESENT);        }    }}

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

HashSet遍历方式

通过Iterator遍历HashSet

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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
// 假设set是HashSet对象for(Iterator iterator = set.iterator();       iterator.hasNext(); ) {     iterator.next();} ```  

## 通过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 : %sn", str);HashSet的遍历测试程序如下: 

```javaimport java.util.Random;import java.util.Iterator;import java.util.HashSet;

/* * @desc 介绍HashSet遍历方法 * * @author skywang */public class HashSetIteratorTest {

    public static void main(String[] args) {        // 新建HashSet        HashSet set = new HashSet();

        // 添加元素 到HashSet中        for (int i=0; i<5; 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 : %sn", iterator.next());        }    }

    /*     * 通过for-each遍历HashSet。不推荐!此方法需要先将Set转换为数组     */    private static void foreachHashSet(HashSet set) {        String[] arr = (String[])set.toArray(new String[0]);        for (String str:arr)            System.out.printf("for each : %sn", str);    }}

运行结果:

12345678910
iterator : 3iterator : 2iterator : 1iterator : 0iterator : 4for each : 3for each : 2for each : 1for each : 0for each : 4

HashSet示例

下面我们通过实例学习如何使用HashSet

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
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 : %dn", set.size());

        // 判断HashSet是否包含某个值        System.out.printf("HashSet contains a :%sn", set.contains("a"));        System.out.printf("HashSet contains g :%sn", set.contains("g"));

        // 删除HashSet中的“e”        set.remove("e");

        // 将Set转换为数组        String[] arr = (String[])set.toArray(new String[0]);        for (String str:arr)            System.out.printf("for each : %sn", 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 : %sn", removeset);

        // 克隆一个retainset,内容和set一模一样        HashSet retainset = (HashSet)set.clone();        // 保留“retainset中,属于otherSet的元素”        retainset.retainAll(otherset);        // 打印retainset        System.out.printf("retainset : %sn", retainset);

        // 遍历HashSet        for(Iterator iterator = set.iterator();               iterator.hasNext(); )             System.out.printf("iterator : %sn", iterator.next());

        // 清空HashSet        set.clear();

        // 输出HashSet是否为空        System.out.printf("%sn", set.isEmpty()?"set is empty":"set is not empty");    }}

运行结果:

1234567891011121314
size : 5HashSet contains a :trueHashSet contains g :falsefor each : dfor each : bfor each : cfor each : aremoveset : [d, a]retainset : [b, c]iterator : diterator : biterator : citerator : aset is empty

原文:大专栏  集合框架之 HashSet

原文地址:https://www.cnblogs.com/petewell/p/11597463.html

时间: 2024-10-21 11:54:51

集合框架之 HashSet的相关文章

[javaSE] 集合框架(HashSet)

Set:元素是无序,不可重复的 HaseSet:底层数据结构是哈希表 定义一个类Demo 获取Demo对象,system.out.println(demo),打印demo对象,[email protected] Demo对象在内存中是按照哈希值存储在哈希表中,取出也是按照哈希值,所以是无序的 import java.util.HashSet; class Demo{ } public class HashSetDemo { /** * @param args */ public static v

深入集合框架之HashSet源码剖析

HashSet实现了Set接口,也就是说它存储的元素是无重复的. 通过源码分析我们可以发现HashSet就是HashMap的一个实例. 因为在HashMap中的键是不能重复的,我们可以把HashSet想象成HashMap中的键,而且事实也就是如此. 接下来我们具体分析一个构造函数: 版权声明:本文为博主原创文章,未经博主允许不得转载.

java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)

/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不一致,元素不可以重复 * (通过哈希值来判断是否是同一个对象) * ----HashSet:底层数据结构是哈希表, * 保证数据唯一性的方法是调用存入元素的hashCode()方法 * 和equals(Object obj)方法 * HashCode值相同,才会调用equals方法 * * */ 1 import java.util.HashSet; 2 import java.util.Iterator; 3 publ

java集合框架之HashSet

参考http://how2j.cn/k/collection/collection-hashset/364.html#nowhere 元素不能重复 Set中的元素,不能重复 package collection; import java.util.HashSet; public class TestCollection { public static void main(String[] args) { HashSet<String> names = new HashSet<String

java集合框架小结(进阶版)之HashSet篇

建议先看下:java集合框架小结(进阶版)之HashMap篇 基本概念: hashSet: 根据java集合框架小结(初级版)图示,HashSet是AbstractSet的一个子类,是基于Hash算法的Set接口的实现,顾名思义.允许添加null. --------------------------------------↑ 以上都是扯淡 ↑,↓ HashSet完全是在挂羊头卖狗肉 ↓------------------------------------------- 何谓挂羊头卖狗肉?大家

Java自学-集合框架 HashSet

Java集合框架 HashSet 示例 1 : 元素不能重复 Set中的元素,不能重复 package collection; import java.util.HashSet; public class TestCollection { public static void main(String[] args) { HashSet<String> names = new HashSet<String>(); names.add("gareen"); Syst

Java基础知识强化之集合框架笔记39:Set集合之HashSet存储字符串并遍历

1. HashSet类的概述: (1)不保证set的迭代顺序 (2)特别是它不保证该顺序恒久不变 2. HashSet如何保证元素唯一性 (1)底层数据结构是哈希表(元素时链表的数组) (2)哈希表依赖于哈希值存储 (3)添加功能底层依赖于两个方法:   • int hashCode()   • boolean equals(Object obj) 3. HashSet存储字符串并遍历: (1)代码示例如下: 1 package cn.itcast_02; 2 3 import java.uti

Java—集合框架List

集合的概念 现实生活中:很多的事物凑在一起 数学中的集合:具有共同属性的事物的总和 Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象 集合的作用 在类的内部,对数据进行组织(针对作用与意义一样的属性,将他们放到一个集合中) 简单而快速的搜索大数量的条目 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中快速的插入或删除有关元素 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型 与数组相比 数组的长度

《深入理解Java集合框架》系列文章

Introduction 关于C++标准模板库(Standard Template Library, STL)的书籍和资料有很多,关于Java集合框架(Java Collections Framework, JCF)的资料却很少,甚至很难找到一本专门介绍它的书籍,这给Java学习者们带来不小的麻烦.我深深的不解其中的原因.虽然JCF设计参考了STL,但其定位不是Java版的STL,而是要实现一个精简紧凑的容器框架,对STL的介绍自然不能替代对JCF的介绍. 本系列文章主要从数据结构和算法层面分析