关于HashSet与HashMap的区别及说明
HashSet与HashMap的区别
HashSet 集合不允许存储相同的元素, 它底层实际上使用 HashMap 来存储元素的,不过关注的只是key元素, 所有 value元素默认为 Object类对象.
HashSet源码如下
HashSet 的构造方法
//HashSet底层用来存储元素的结构,实际上使用HashMap来存储 private transient HashMap<E,Object> map; //HashMap中的value值,HashSet只关注key值,所以所有的value值都为Object对象 private static final Object PRESENT = new Object(); //HashSet的无参构造,直接创建了一个HashMap对象 public HashSet() { map = new HashMap<>(); } //指定初始化容量和负载因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //给定初始化容量 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); }
可以看到 HashSet的构造方法底层都是调用 HashMap的构造方法, 所以HashSet底层实际上是使用 HashMap 来作为存储结构.
当使用无参构造创建 HashSet对象时, 其实调用了 HashMap的无参构造创建了一个 HashMap对象, 所以 HashSet 的初始化容量也为16, 负载因子也为 0.75.
再来看看 HashSet 的 add() 方法的实现:
可以看到 HashSet 的 add() 方法底层实际也是调用了 HashMap 的 put() 方法, 这里的key为我们传入的将要添加到 set集合中的元素, 而value值则为 PERSENT,其实就是上面分析的 HashSet类中的一个静态字段, 默认为 Object对象.
HashSet并不关注value元素, 只使用 HashMap来存储 key元素, 这就使得 HashSet判断元素相等的条件与 HashMap中 key相等的条件其实是一样的, 两个元素的 hashCode值相同且通过equals()方法比较返回 true.
所以HashSet应该重写 equals()和hashCode()方法, 两个元素的 HashCode相同, 保证通过equals() 方法比较返回 true.
总结一下HashSet和HashMap的区别
(1)HashSet实现了Set接口, 仅存储对象; HashMap实现了 Map接口, 存储的是键值对.
(2)HashSet底层其实是用HashMap实现存储的, HashSet封装了一系列HashMap的方法. 依靠HashMap来存储元素值,(利用hashMap的key键进行存储), 而value值默认为Object对象. 所以HashSet也不允许出现重复值, 判断标准和HashMap判断标准相同, 两个元素的hashCode相等并且通过equals()方法返回true.
HashSet与HashMap的关系
HashSet作为一种最简单的java集合类,真的可以用三句话来概括一下:
第一句:存放不重复的数据。第二句:底层基于hash表实现。第三句:内部基于HashMap。
这也就是说,你想要完完全全彻彻底底地把HashSet吃透,就一定要先吃透HashMap。这篇文章将带着你从特点到存储,再到最后的实现,从源码角度来分析一下。
认识
HashSet其实就是一个没有重复数据的集合,基本用法很简单,我们直接给个例子。
以上只是列出了其最简单的用法。下面我们看看其继承关系。
HashSet主要继承了三个接口Serializable、Cloneable、Set,并且实现了抽象类AbstractSet。
我们直接看看源码:
学过HashMap的人应该都知道HashMap实现的是Map接口,而HashSet是Set接口。
下面我们就从源码的角度来分析一下HashSet。
源码分析
1、参数变量
这里有个问题,那就是既然HashSet只使用到了HashMap的key,为什么不使用null来充当HashMap的value,而使用了PRESENT这个对象呢?
答:想要深入这个问题,我们还需要深入到源码中看看:
以上两个是增删方法,在add一个元素的时候,其实调用的就是map.put(e, PRESENT)==null,HashMap在put元素的时候会出现两种情况:
情况一:put的元素是新的,那么map.put会发现key没有,那么直接插入即可。return结果为true。
情况二:put的元素是旧的,那么map.put会发现key已有,则直接返回相应的value,也就是PRESENT,PRESENT不等于null,return的也就是false了,表示HashSet插入失败。如果我们这里使用null为map.put的参数呢?直接返回相应的value,也就是null,这时候null==null是true。竟然返回了true。很明显就是错误的返回结果呀。
这其实也是去重复的原理。对于删除方法其实也是一样的。
2、构造函数
HashSet提供的构造方法很多,有5个,在这里我想说明的是每一种构造方法,其实都是创建的HashMap。这也证明了我们文章开头提到的内部基于HashMap。
3、其他方法
增删方法我们已经提到了,在这里我们主要看一下其他方法。
上面的方法还包含了遍历元素的方式。
HashSet就是这么简单,源码里面几乎所有的方法都是HashMap实现的。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
解决 java: 程序包com.baomidou.mybatisplus.annotation不存在
在使用Java编写程序时,经常会遇到各种编译错误或运行时异常,本文主要介绍了解决java:程序包com.baomidou.mybatisplus.annotation不存在,具有一定的参考价值,感兴趣的可以了解一下2024-03-03Spring Boot security 默认拦截静态资源的解决方法
这篇文章主要介绍了Spring Boot security 默认拦截静态资源,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-03-03httpclient getPoolEntryBlocking连接池方法源码解读
这篇文章主要为大家介绍了httpclient getPoolEntryBlocking连接池方法源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-11-11
最新评论