关于HashSet与HashMap的区别及说明
作者:huhahuha_
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实现的。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。