上篇文章《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. Jmeter中察看结果树中的响应数据,中文显示乱码问题处理

    打开apache-jmeter-xxx\bin\jmeter.properties文件,搜索"encoding"关键字,找到如下配置: # The encoding to be u ...

  2. java获取获得Timestamp类型的当前系统时间。以及java.util.date 、java.sql.Date之间的转换

    java获取取得Timestamp类型的当前系统时间java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = new T ...

  3. IPC

    IPC,全名Inter Process Communication即进程间通讯,在同一台机器上的两个进程就用IPC,不能跨物理机器。IPC包括共享内存、队列、信号量等几种方式,由于IPC通讯效率之高, ...

  4. swif tableview全选

    func selctAll() { idArr.removeAll() for var i = 0; i<sellingArr.count; i++ { let path: NSIndexPat ...

  5. POJ 2876 Cantoring Along

    Description The Cantor set was discovered by Georg Cantor. It is one of the simpler fractals. It is ...

  6. 20160330001 调用及触发Office Outlook 约会

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  7. zigbee学习之路(五):定时器1(查询方式)

    一.前言 今天,我们来学习几乎所有单片机都有的功能,定时器的使用,定时器对单片机来说是相当重要的,有了它,单片机就可以进行一些复杂的工作. 二.原理与分析 谈到定时器的控制,我们最先想到的是要给它赋初 ...

  8. (转)将cocos2dx项目从VS移植到Eclipse

    本文转自:http://www.cnblogs.com/Z-XML/p/3349518.html 引言:我们使用cocos2d-x引擎制作了一款飞行射击游戏,其中创新性地融入了手势识别功能.但是我 们 ...

  9. noi 2985 数字组合

    题目链接: http://noi.openjudge.cn/ch0206/2985/ 2985:数字组合 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB 描述 有n个正 ...

  10. js图片放大镜

    <!doctype html><html><head><meta charset="utf-8"><style>#sma ...