java-基础-集合hashset

HashSet 简介

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

 // 默认构造函数
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) 
boolean         add(E object)
void            clear()
Object          clone()
boolean         contains(Object object)
boolean         isEmpty()
Iterator<E>     iterator()
boolean         remove(Object object)
int             size()
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 { }

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

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

HashSet的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过HashMap实现的

// 假设set是HashSet对象
for(Iterator iterator = set.iterator();
       iterator.hasNext(); ) {
    iterator.next();
}   
// 假设set是HashSet对象,并且set中元素是String类型
String[] arr = (String[])set.toArray(new String[0]);
for (String str:arr)
    System.out.printf("for each : %s\n", str);
import java.util.Random;
import java.util.Iterator;
import java.util.HashSet;
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 : %s\n", 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 : %s\n", str);
    }
}
import java.util.Iterator;
import java.util.HashSet;

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[0]);
        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");
    }

}
时间: 2025-01-25 19:22:19

java-基础-集合hashset的相关文章

Java基础-17总结,登录注册案例,Set集合,HashSet,TreeSet,LinkHashSet

你需要的是什么,直接评论留言. 获取更多资源加微信公众号"Java帮帮" (是公众号,不是微信好友哦) 还有"Java帮帮"今日头条号,技术文章与新闻,每日更新,欢迎阅读 学习交流请加Java帮帮交流QQ群553841695 分享是一种美德,分享更快乐! 1:登录注册案例(理解) 需求:用户登录注册案例. 按照如下的操作,可以让我们更符号面向对象思想 A:有哪些类呢? B:每个类有哪些东西呢? C:类与类之间的关系是什么呢? 分析: A:有哪些类呢? 用户类 测试类

Java基础-15总结对象数组,集合Collection,集合List

你需要的是什么,直接评论留言. 获取更多资源加微信公众号"Java帮帮" (是公众号,不是微信好友哦) 还有"Java帮帮"今日头条号,技术文章与新闻,每日更新,欢迎阅读 学习交流请加Java帮帮交流QQ群553841695 分享是一种美德,分享更快乐! 1:对象数组(掌握) (1)数组既可以存储基本数据类型,也可以存储引用类型.它存储引用类型的时候的数组就叫对象数组. (2)案例: 用数组存储5个学生对象,并遍历数组. package cn.itcast_01;

Java基础之集合

版权声明:本文为博主原创文章,转载注明出处http://blog.csdn.net/u013142781 目录(?)[+] 最近看泛型的时候,刚好看到集合相关的,所以想把一些常用集合类型的思路理清一下. 参考文章: http://android.blog.51cto.com/268543/400557/ http://www.cnblogs.com/xwdreamer/archive/2012/05/30/2526822.html http://blog.csdn.net/softwave/ar

JavaWeb基础教程之Java基础加强版_java

1.myeclipse的安装和使用 * eclipse:是一个免费的开发工具 * myeclipse:是一个收费的插件,破解myeclipse, ** 安装目录的要求: 不能有中文和空格 ** 安装完成之后,选择一个工作空间 ,这个工作空间不能有中文和空格 * 破解myeclipse ** 运行run.bat文件,但是运行之前,必须要安装jdk,通过配置环境变量 * myeclipse的使用 * 创建一个工程 - 类型 java project web project - 选择依赖的jdk,可以

程序猿的日常——Java基础之equals与hashCode

equals和hashCode是我们日常开发最常使用的方法,但是因为一般都使用默认的规则,因此也很少会引起关注.不过了解他们的用途和设计的原则,还是会帮助我们更好的设计代码. equals equals是java很基础的一个问题,通常都会跟==来做比较.那么看看下面的问题: int a = 1; int b = 1; System.out.println(a==b);//true Integer a1 = new Integer(1); Integer a2 = new Integer(1);

Java 语言集合架构(Set规则集)

集合中存在的是没有重复元素的对象的序列. Java提供了相应的类和接口存储这样的元素--Set规 则集. 下面以一个例子学习Set集合架构,其中包含注释: package com.brucezhang.test; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; public class SetTest { /** * @param

Github优秀java项目集合(中文版) - 涉及java所有的知识体系 -- good

Java资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站点等等.伯乐在线已经把 awesome-java 资源列表翻成中文后发布于 ImportNew. Awesome 系列虽然挺全,但基本只对收录的资源做了极为简要的介绍,如果有更详细的中文介绍,对相应开发者的帮助会更

Java基础之001-学习资源的选择

Java基础之001-学习资源的选择                                                                                                                            35岁学习Java       1. 工欲善其事,必先利其器        学习是一种能力.站在巨人的肩膀上,才能站的更高,走的更远.为了学习java,我在网络上探寻了很长时间,最终选择了传智播客的免费教学视频(绝非

给Java新手的一些建议——Java知识点归纳(Java基础部分)

写这篇文章的目的是想总结一下自己这么多年来使用java的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享给刚刚入门的Java程序员和打算入Java开发这个行当的准新手们,希望可以给大家一些经验,能让大家更好学习和使用Java. 这次介绍的主要内容是和J2SE相关的部分,另外,会在以后再介绍些J2EE相关的.和Java中各个框架相关的内容. 经过这么多年的Java开发,以及结合平时面试Java开发者的一些经验,我觉得对于J2SE方面主要就是要掌握以下的一些内容. 1. JVM相

给Java新手的一些建议——Java知识点归纳(Java基础部分)

写这篇文章的目的是想总结一下自己这么多年来使用java的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享给刚刚入门的Java程序员和打算入Java开发这个行当的准新手们,希望可以给大家一些经验,能让大家更好学习和使用Java. 这次介绍的主要内容是和J2SE相关的部分,另外,会在以后再介绍些J2EE相关的.和Java中各个框架相关的内容. 经过这么多年的Java开发,以及结合平时面试Java开发者的一些经验,我觉得对于J2SE方面主要就是要掌握以下的一些内容. 1. JVM相