上篇文章《HashMap其实就那么一回事儿之源码浅析》介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap, 再来看HashSet的源码,会觉得极其简单。下面还是直接看源码吧:

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L; //HashMap ? 没错,HashSet就是通过HashMap保存数据, HashSet的值就是HashMap的key
private transient HashMap<E,Object> map; //HashMap 为<key, value>的键值对, 既然HashSet的值就是HashMap的key, 那么HashMap的值呢,当然就是这个PRESENT啦
private static final Object PRESENT = new Object(); //下面这一系列的构造方法都是创建HashMap, 之前已经介绍过HashMap, 这儿就不再详说了
public HashSet() {
map = new HashMap<>();
} //将一个已知的collection转换为HashSet
public HashSet(Collection<? extends E> c) {
//这儿的HashMap的参数为什么这么写?
//上次介绍HashMap的时候可知,如果没有指定HashMap的capacity, 那么默认的就是16
//根据 threshold = capacity * loadFactor, 可以计算出 capacity
//Math.max((int) (c.size()/.75f) + 1, 16) 这个意思就是capacity如果没超过16, 那么就直接使用默认的16
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
//将已知的collection转换为HashSet的方法
//addAll方法是HashSet的父类AbstractCollection的方法,为了便于阅读,会将代码粘贴在下面
addAll(c);
} public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
} public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
} HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
} //addAll方法是HashSet的父类AbstractCollection的方法
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
//此处的add方法由HashSet重写实现
if (add(e))
modified = true;
return modified;
} //HashSet的核心方法来了, 没错,就这么简单
public boolean add(E e) {
//应证了上面所说的key为HashSet的值
return map.put(e, PRESENT)==null;
} //剩下这些方法都是跟Map相关的了,只要熟悉了HashMap, 那就太简单了,就不说了
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
} public void clear() {
map.clear();
} }

就这样,HashSet的源码如此简单。下面还是对HashSet的源码作一个总结吧:

1. HashSet基于HashMap实现, 以HashSet的值作为HashMap的一个key, 以一个Object对象常量作为HashMap的值。

2. 根据HashMap的特性,可以推敲出:HashSet允许拥有1个为null的值, HashSet的值不可重复。

3. 在创建HashSet的时候,如果合适,最好指定其内部HashMap的 capacity和loadFactory的值, 至于原因,在介绍HashMap的时候,提到过。

OK, 讲完HashSet之后,我觉得是时候提一下这个问题了: 可能在大家初学java的时候,老师或者书上都推荐大家在重写对象equals的时候,最好重写一下hashCode方法,还记得吧? 为什么要这么做? 给大家演示一下,你就能明白了,下面看一个小demo:

先定义一个Person类:

public class Person {

    //身份证
private String idCard; private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getIdCard() {
return idCard;
} public void setIdCard(String idCard) {
this.idCard = idCard;
} //重写equals方法(规则是:idCard一致,则认为是同一个人)
@Override
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(!(obj instanceof Person)) {
return false;
}
Person others = (Person) obj;
if(others.getIdCard().equals(idCard)) {
return true;
}
return false;
}
}

然后,写一个测试类,用HashSet去添加Person实例:

public class Test {

    public static void main(String[] args) {

        Person p1 = new Person();
p1.setIdCard("1234567890"); Person p2 = new Person();
p2.setIdCard("1234567890"); Set<Person> hashSet = new HashSet<Person>();
hashSet.add(p1);
hashSet.add(p2); System.out.println(hashSet.size()); } }

我们知道HashSet的元素不可重复,因此,在以上测试代码中,p1 与 p2对象是equals的,我们本来希望HashSet只保存其中一个对象, 但是,事与愿违,输出的结果却是2, 说明hashSet把这两个对象都保存了。这是为什么呢? 我们结合一下HashMap来看吧, 首先,由于我们没有重写Person的hashCode方法,会导致p1 与 p2的hash值不一致,这时, HashMap会把hash不一致的元素放在不同的位置, 因此就产生了两个对象。那么,怎么改善? 当然是重写hashCode方法了。下面,我们在Person类中,重写hashCode方法:

@Override
public int hashCode() {
return this.idCard.hashCode() * 11;
}

这时候,我们再用上面的测试类测试,发现输出为1。OK,终于和我们的想法一致了。这就是为什么强烈推荐在重写equals方法的时候,同时重写hashCode方法的原因之一。

好了,本次就写到此。谢谢大家!

HashSet其实就那么一回事儿之源码浅析的更多相关文章

  1. HashMap其实就那么一回事儿之源码浅析

    上篇文章<LinkedList其实就那么一回事儿之源码分析>介绍了LinkedList, 本次将为大家介绍HashMap. 在介绍HashMap之前,为了方便更清楚地理解源码,先大致说说H ...

  2. 【转】ArrayList其实就那么一回事儿之源码浅析

    转自:http://www.cnblogs.com/dongying/p/4013271.html?utm_source=tuicool&utm_medium=referral ArrayLi ...

  3. ArrayList其实就那么一回事儿之源码浅析

    ArrayList 算是常用的集合之一了,不知作为javaner的你有没在百忙之中抽出一点时间看看ArrayList的源码呢. 如果看了,你会觉得其实ArrayList其实就那么一回事儿,对吧,下面就 ...

  4. LinkedList其实就那么一回事儿之源码分析

    上篇文章<ArrayList其实就那么一回儿事儿之源码分析>,给大家谈了ArrayList, 那么本次,就给大家一起看看同为List 家族的LinkedList. 下面就直接看源码吧: p ...

  5. HashSet实现不重复储值原理-附源码解析

    在HashSet中,基本的操作都是由HashMap底层实现的,因为HashSet底层是用HashMap存储数据.当向HashSet中添加元素的时候,首先计算元素的hashcode值,然后用这个(元素的 ...

  6. Code Reading: ORB-SLAM回环检测源码阅读+注释

    之前研究过一些回环检测的内容,首先要看的自然是用词袋回环的鼻祖和正当继承人(没有冒犯VINS和LDSO的意思)ORB-SLAM.下面是我的代码注释.因为代码都是自己手打的,不是在源码上注释的,所以一些 ...

  7. epoll 回显服务器源码

    在写epoll回显服务器代码之前,可以先看看上一篇文章:select poll epoll三者之间的比较.最近在继续学习网络编程中的服务端编程中,了解到很多网游服务器是在IOMP(IO完成端口)框架下 ...

  8. 日志那点事儿——slf4j源码剖析

    前言: 说到日志,大多人都没空去研究,顶多知道用logger.info或者warn打打消息.那么commons-logging,slf4j,logback,log4j,logging又是什么关系呢?其 ...

  9. java集合-HashSet源码解析

    HashSet 无序集合类 实现了Set接口 内部通过HashMap实现 // HashSet public class HashSet<E> extends AbstractSet< ...

随机推荐

  1. delphi学习笔记1

    快捷键CTRL+ENTER 定位到单元文件 F6快速查找文件 uses语句和include 指令 C++程序员应该知道uses语句和include 指令是不同的.uses语句只是用于输入引用单元的预编 ...

  2. 在Linux最小系统上编译运行第一个helloworld程序

    一.安装和使用SSH软件 1.安装SSH 软件 1)SSH 软件压缩包可以在网盘下载,下载后解压,进入解压出来的文件夹,如下图. 2)单击上图中的“SSHSecureShellClient-3.2.9 ...

  3. linux 下查看系统资源和负载,以及性能监控

    1,查看磁盘   df -h   2,查看内存大小   free   free [-m|g]按MB,GB显示内存   vmstat   3,查看cpu   cat /proc/cpuinfo   只看 ...

  4. Salesforce select字段的多少对性能影响巨大

    Salesforce select字段的多少对性能影响巨大,第1个是select 144个字段,第2个是select 5个字段, 性能相差了7倍 "select Id,IsDeleted,M ...

  5. WGS84坐标和UTM坐标的转换

    如题.做了一个Demo,主要是把最后面的参考资料1里面的脚本改成了C语言版本的. 代码: #ifndef __COORCONV_H__ #define __COORCONV_H__ #include ...

  6. js数组去重的4个方法

    面试前端必须准备的一个问题:怎样去掉Javascript的Array的重复项, 这个问题看起来简单,但是其实暗藏杀机. 考的不仅仅是实现这个功能,更能看出你对计算机程序执行的深入理解. 我总共总结4种 ...

  7. 数据库的Index Scan V.S. Rscan

    一直在做performance,但直到今天才完成了这个第一天应该完成的图,到底Index scan和Rscan的分界点在哪里?   如下图所示,很简单的一个查询,只是查询int,分别强制走索引和表扫描 ...

  8. RQNOJ Bus

    H城是一座小城市,前几日才刚刚建立公交系统,且只有一辆公交车.于是,如何最大化这唯一一辆公交车的载客量成了亟待解决的问题. H城的俯视图可以近似地看成是一个棋盘网络——共有N行M列,从南向北,每行从1 ...

  9. Qt报表控件NCReport教程:报表创建示例

    NCReport是 一款10多年时间的老牌报表控件,最初是在2002年时作为qt3的应用程序的一个联合项目,后来就成为了一个独立的GPL项目.现在的NCReport 是一款轻量级.快速.多平台.简单易 ...

  10. 使用 supervisor 管理进程

    安装: # yum install python-setuptools # easy_install supervisor 如果已经安装了epel和python-pip, 也可以简单 pip inst ...