对HashMap.put()和HashSet.add()的探究

Java数据结构---HashMap(哈希表及其基本操作)(含hashset) Java数据结构---HashMap(哈希表及其基本操作)1. HashMap简介2. 基础操作(增、删、改、查)3. 其他操作 1. HashMap简介 HashMap 是一个散列表,它存储的内容是键值(key-value)映射。 HashMap 的 key 与 value 类型可以相同也可以不同,根据定义,不受限制。 2. 基础操作(增、删、改、查) 定义一个哈希表 HashMap<Integer, String> hashmap= new HashMap<Integer, Str 阅读详情


这篇文章主要讨论HashMap.put()和HashSet.add()其中的几点关系和特性,即

1.HashSet的底层调用了HashMap

2.HashMap插入的entry允许key=null,value=null,HashSet也允许添加null对象

3.HashMap中entry的key不会有重复

以下纯粹是自己在阅读相关源码和资料后自己的总结,由于自己的水平有限,如果有任何错误的地方,希望大家能够指出并一起讨论解决。




1.HashSet的底层调用了HashMap

HashSet和HashMap一个重要的关系就是HashSet的底层是调用了HashMap,既然此篇文章探究的是HashMap.put()和HashSet.add()的关系,那就以HashSet.add()方法来举例(似乎不是很严谨),如:

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

再来看map这个实例对象

private transient HashMap<E,Object> map;

从上述源码中,HashSet和HashMap的关系便一目了然




2.HashMap插入的entry允许key=null,value=null,HashSet也允许添加null对象

既然HashMap和HashSet有这么密切的关系,那么它们在方法的某些特性上必有相似的特点,那就先从HashMap.put()说起,先看一段代码:

public class Test {
   	
	public static void main(String[] args){

		HashMap<String,String> hashMap = new HashMap<String,String>();
		System.out.println("这是插入key=null,value=null的Node之前hashMap的长度:"+hashMap.size());
		hashMap.put(null, null);
		System.out.println("这是插入key=null,value=null的Node之后hashMap的长度:"+hashMap.size());
	
	}		
	
}

再看看运行结果打印的日志

从结果可以看到key=null,value=null的键值对成功插入到HashMap中


再来看看在HashMap的源码中是如何实现这一点的

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

先看其中的hash()方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

从中可以看出key是允许==null,当key==null时hash值便为0

再看看putVal()方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

其中并没有看到对key==null或者value==null时的限制,其中有一段重要的代码

 if ((p = tab[i = (n - 1) & hash]) == null)

从上面的讨论中已知当key==null时hash的值为0,所以i也为0,也就是说当key==null时,对应的entry是安插在索引位置为0,即数组的第一个节点

因此可以得出结论,HashMap的键值对的key和value是允许为null的,由于HashSet.add()的底层是调用HashMap,同理可知HashSet也是允许添加进值为null的对象





3.HashMap中entry的key不会有重复

先看一段关于插入的代码

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {                                             //说明此时在对应的索引位置已经有对象了
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) // 判断原对象与插入的对象的key是否一样
                e = p;
            else if (p instanceof TreeNode)
               //......
            else {             
               //......
}
            if (e != null) { //说明原对象与插入的对象的key相同 
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;//将新插入的entry的value覆盖掉原来的entry的value
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }


从上述代码可以看出,当插入的entry与已插入的老entry的key和hash都相同时(即哈希冲突),因为key相同,所以对key不进行更改操作,而新entry的value将会覆盖掉老entry的value(即oldValue),因此一个HashMap中不会有重复的key,HashSet恰恰是利用HashMap的这一特性来使得HashSet中的对象没有重复的,先看一段HashSet源码

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }



再看看PRESENT对象

private static final Object PRESENT = new Object();


从中可以看出,当HashSet每次add一个对象时都调用map.put,之前的讨论中已知e也就是key是不会重复的,

而value每一次都用PRESENT这个对象代替,因此实现了HashSet不会有重复的元素这个特性



HashSet的add方法 我们使用过ArrayList类的add方法,其add方法是可以任意添加元素的,而HashSet的add方法则会限制添加重复元素,那么它是如何做到的呢?这需要我们通过查看底层代码来研究。 import java.util.HashSet; public class HashSetLearn { public static void main(String[] args) { HashSe... 阅读详情

相关推荐

【SEED Labs 2.0】V*N Tunneling Lab

本文为 SEED Labs 2.0 - V*N Tunneling Lab 的实验记录。本实验工作量较大,但难度不大,重点在于搞清是谁发给谁,走的什么路径。另外,本次实验由于网络中的设备较多,修改了环境变量方便分清哪个窗口是哪个设备。这样操作起来就方便多了。...

Chenyang的博客 6974

Java高级之HashMap中的keySet()方法

keySet()方法的源码为: public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new KeySet(); keySet = ks; } return ks; } 注释为: /** * 获取HashMap的键的集合,以Set<K>保

二木成林 1万+

实验五 循环结构第1关:斐波那契数列第2关:统计一段英文语句中的单词数和句子数第3关:密码判断第4关:通过数列求自然常数e第5关:双倍余额递减法计算固定资产折旧第6关:销售统计第7关:投资年限计算

第1关:斐波那契数列第2关:统计一段英文语句中的单词数和句子数第3关:密码判断 第4关:通过数列求自然常数e 第5关:双倍余额递减法计算固定资产折旧

pro_daily的博客 8168

Java HashMap keySet()方法与示例

HashMap类的keySet()方法 (HashMap Class keySet() method) keySet() method is available in java.util package. keySet()方法在java.util包中可用。 keySet() method is used to return a set of keys exist in this HashMap ...

1217

linux上使用tun/tap设备模拟一个虚拟网卡,并将该虚拟网卡连接到新创建的网桥上

tun/tap设备是操作系统内核中的虚拟网络设备,是用软件模拟的网络设备,提供与硬件网络设备完全相同的功能。主要用于用户空间和内核空间传递报文。tun/tap对于硬件网络设备而言,一端连接的是物理网络,一端连接的是网络协议栈。对于tun/tap设备而言,一端连接的是应用程序(通过 字符设备文件),一端连接的是网络协议栈。

糖果墙的博客 6411

投第一篇SCIE英文期刊(详细步骤)

一个投稿SCIE英文期刊的过程

weixin_48849679的博客 1万+

HashSet/HashMap/add()/put()源码详解

先码这

qq_40979103的博客 1552

UG NX二次开发(C#)-建模-获取圆柱面信息(轴向、半径、深度)

获取圆柱面的信息前言一、在UG NX中面的类型有哪些?二、获取圆柱面信息的函数1.获取半径和轴向2、计算圆柱面的深度值3、测试用例总结 文章目录前言一、在UG NX中面的类型有哪些?二、获取圆柱面信息的函数1.获取半径和轴向2、计算圆柱面的深度值3、测试用例总结 前言 在一个项目中,需要遍历曲面,获得所有的圆柱面和圆柱面的信息,包括轴向、半径和圆柱面的深度。经过研究,采用uFModl.AskFaceData(face.Tag, out faceTypeInt, point, dir, box, out

GimiGimmy 4145

HashMap的添加元素过程以及其put方法

3.添加数据的时候,根据所存储的键的hash值计算出应存入数据的下标来确定数据所处位置,当这个位置原本没有数据的时候,则会直接将数据存入,如果原位置有值,且此时数据与原数据的hash值相等,且原本key的值本身与将要添加的key值相等,则对原值进行覆盖,并且此时的put方法返回值由null变为被此时元素的value。put方法就是HashMap在添加元素的时候用到的方法,也是面试中常常考察到的一点,先给大家以八股文的角度回答一下关于put方法的面试问题,这样有助于大家理解后面的源码。

y1490064286的博客 2263

海格通信嵌入式面试题及参考答案

计算电路的最高工作频率如何计算? 计算电路的最高工作频率主要考虑电路中的关键路径延迟。关键路径是指在整个电路中,信号传播延迟最长的路径。电路的最高工作频率的倒数就是时钟周期,而时钟周期必须大于关键路径的延迟时间。 首先要确定电路中各个模块的延迟。比如对于组合逻辑电路,可以通过查看芯片手册获取门电路的延迟参数。以一个简单的与门、或门、非门组成的组合逻辑为例,每个门都有自己的传输延迟,把信号从输入到输出经过的所有门的延迟相加,就得到这部分组合逻辑的延迟。 对于包含寄存器的电路,还需要考虑寄

大模型大数据攻城狮的专栏 1271

BES2700的PMU

BES2700ZP-BES2700芯片的PMU架构分析摘要:本文档分析了BES2700芯片的电源管理单元(PMU)架构。实际应用中仅需为VBAT引脚供电,其余电源均由芯片内部PMU提供。文档包含架构示意图、问题列表和参考链接,涉及PMU工作原理、外部供电要求等关键技术点。待解决问题包括具体供电参数和内部电源转换机制等。通过添加日志信息和代码片段,为开发人员提供了调试PMU功能的实用方法。

云淡风轻 117

hashMap和HashSet的put()操作

hashMap和HashSet的put()操作

weixin_58263930的博客 979

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

java的new HashSet()及HashSet的add()方法底层源码实现的第一次简单解析

1.new HashSet() HashSet<Object> hashSet = new HashSet<>(); 2.进入HashSet()方法 /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */

夏虫不可语寒的博客 4069

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

HashMap与HashSet的区别:Java面试必备知识点

本文对比分析了Java中HashMap和HashSet的核心区别。HashMap实现Map接口存储键值对,使用put()方法添加元素,通过键对象计算哈希值,不允许重复键但允许重复值;HashSet实现Set接口仅存储对象,使用add()方法添加元素,通过成员对象计算哈希值,不允许任何重复元素。关键区别在于HashSet内部实际通过HashMap实现(元素作为键,固定对象作为值)。使用场景上,HashMap适合键值映射和快速查找,HashSet适合去重和集合运算

ywl470812087的博客 10万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

HashSet中add方法的执行过程

1.先对HashMap中的hash方法进行分析,为接下来的add方法的分析作铺垫 //记得用ctrl+shift+o引包 static int hash(Object key) { //这里把HashMap中的hash方法提取了出来放在本类里,便于执行后对其分析 int h; return (key == null) ? 0 : (h = key.hashCo...

CandidBoy的博客 324

hashmap hashset java_java - HashSet和HashMap之间的区别?

java - HashSet和HashMap之间的区别?除了HashSet不允许重复的是,HashMap和HashSet之间的区别是什么?我的意思是实施明智? 它有点模糊,因为它们都使用哈希表来存储值。21个解决方案279 votesHashSet是一组,例如{1,2,3,4,5}HashMap是一个关键 - &gt; 值(键值)映射,例如 {a - &gt; 1,b - &gt; 2,c...

weixin_33196555的博客 850

HashSet底层HashMap的value为何是PRESENT?

HashSet底层的HashMap所存的value为什么不是null?而是一个静态常量PRESENT? HashSet底层add方法 底层源码 public boolean add(E e) { return map.put(e, PRESENT)==null; } 可以看出底层是调用了HashMap的put方法,我们知道HashMap的put方法的返回值为null或者value。如果put成功的话返回null,如果put失败的话,说明put的key已经存在了,就会返回已经存在该k

missbearC的博客 4011

HashSet集合中add()的执行过程详解

一、hash方法 在进行add之前要知道hash方法对各种数据类型的操作规则 public class Test1 { static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } p...

Zzze0101的博客 1580

【JAVA】hashMap和hashSet的区别

1. hashMap 主要对外接口: clear() clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。 containsKey(key) containsKey() 的作用是判断HashMap是否包含key。 HashMap&lt;Integer, Integer&gt; hashMap=new HashMap&lt;&gt;(4); hashMa...

7247

详述HashSet中add方法

一、HashSet中对象的创建 在分析HashSet的add方法的底层代码之前我们先要理解HashSet集合中的对象是如何创建的。 先创建一个JavaWeb工程,引入hashset的包,创建含有HashSet类的方法,然后就可以进入HashSet的底层代码: package hashset; import java.util.HashSet; public class Test { ...

x_j_k_jysg的博客 5466

Hashset as HashMap add方法

public class MapTest {  public static void main(String[] args) {   Map m =new HashMap();   m.put("1", 2);   Object i  = m.get("1");   System.out.println(m.put("1", 3));   System.out.println(m.ge

xiaomin1991222的专栏 469

HashSet和HashMap

java.util.HashSet 实现了的接口: Serializable,Cloneable,Iterable,Collection,Set 直接子类: JobStateReasons,LinkedHashSet boolean add(E e): 在Set中添加元素,当元素值重复时会返回false,如果成功添加的话会返回true。 void clear() Object clone()...

zjpp2580369的博客 193

JAVA学习心得——详述HashSet中的.add();方法

下列代码是向set集合中添加元素。 public class Test { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("Tom"); 一个简单的.add方法,但是源码有很多值得耐人寻味的东西。 首先: HashSet<Str...

low1101的博客 6276

HashSet中add()方法的执行过程分析

HashSet中add()方法的执行过程分析 一、在HashSet中添加一个String类型的数据,具体步骤如下: HashSet<String> set = new HashSet<>(); set.add("Tom");//HashSet存储的数据实际上存在了HashMap的key中 //(1) set.add("Tom");//(2) set.add("Lu...

zzxv587的博客 309

如何完美解答面试问题——HashMap和HashSet的区别

 大家好,我是孤焰。今天要谈一谈在面试过程中几乎被每个面试官都会提到的一个问题——HashMap和HashSet的区别是什么?  由于我也是刚刚学习编程的小白,所以此篇博文将参考了多篇博文,最后总结而成。 1.什么是HashMap? 1.1什么是HashMap?  HashMap是存储Key-Value键值对的集合,每个键值对也叫做Entry,这些Entry分散存储在一个数组中,这个数组可以称是HashMap的主干。  HashMap中的每一个元素的初始值都是NULL,且默认数组长度是16 HashM.

GuYan的博客 1440
下一篇: ArrayList源码剖析,解开神秘面纱之扩容(一)
ZengBlog
博客等级 码龄10年 29粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值