博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
三句话都能解释清楚的java集合类HashSet,你清楚吗?
阅读量:4034 次
发布时间:2019-05-24

本文共 3031 字,大约阅读时间需要 10 分钟。

题目:三句话都能明白的java集合类HashSet,你都知道吗?

HashSet作为一种最简单的java集合类,真的可以用三句话来概括一下:

第一句:存放不重复的数据。

第二句:底层基于hash表实现。

第三句:内部基于HashMap。

这也就是说,你想要完完全全彻彻底底地把HashSet吃透,就一定要先吃透HashMap。这篇文章将带着你从特点到存储,再到最后的实现,从源码角度来分析一下。

一、认识

HashSet其实就是一个没有重复数据的集合,基本用法很简单,我们直接给个例子。

public class Test {
public static void main(String[] args) {
HashSet set = new HashSet(); // 将元素添加到Set中 set.add("a"); //加入一个存在的则会替换。 set.add("a"); //是否包含某个值 System.out.println("是否包含了a:", set.contains("a")); // 删除HashSet中的“e” set.remove("e"); // 将Set转换为数组 String[] arr = (String[])set.toArray(new String[0]); // 遍历HashSet for(Iterator iterator = set.iterator();iterator.hasNext();) System.out.println(iterator.next()); // 清空HashSet set.clear(); }}

以上只是列出了其最简单的用法。下面我们看看其继承关系。HashSet主要继承了三个接口Serializable、Cloneable、Set,并且实现了抽象类AbstractSet。我们直接看看源码:

public class HashSet
extends AbstractSet
implements Set
, Cloneable, java.io.Serializable

学过HashMap的人应该都知道HashMap实现的是Map接口,而HashSet是Set接口。

下面我们就从源码的角度来分析一下HashSet。

二、源码分析

1、参数变量

//这个HashMap就是实际保存HashSet元素的容器private transient HashMap
map;//PRESENT表示的意思很简单,也就是我们的HashSet只使用到了HashMap的key,//所以此处定义一个静态的常量Object类,来充当HashMap的valueprivate static final Object PRESENT = new Object();

这里有个问题,那就是既然HashSet只使用到了HashMap的key,为什么不使用null来充当HashMap的value,而使用了PRESENT这个对象呢?

答:想要深入这个问题,我们还需要深入到源码中看看:

public boolean add(E e) {
return map.put(e, PRESENT)==null;}public boolean remove(Object o) {
return map.remove(o)==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、构造函数

public HashSet() {
map = new HashMap
();}public HashSet(Collection
c) {
map = new HashMap
(Math. max((int) (c.size()/.75f) + 1, 16)); 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);}

HashSet提供的构造方法很多,有5个,在这里我想说明的是每一种构造方法,其实都是创建的HashMap。这也证明了我们文章开头提到的内部基于HashMap。

3、其他方法

增删方法我们已经提到了,在这里我们主要看一下其他方法。

//底层利用的还是HashMappublic boolean contains(Object o) {
return map .containsKey(o);}//检查是否包含指定集合中所有元素public boolean containsAll(Collection
c) {
Iterator
e = c.iterator(); //只要集合c中有一个元素不属于HashSet,返回false while (e.hasNext()) if (!contains(e.next())) return false; return true;}

上面的方法还包含了遍历元素的方式。

HashSet就是这么简单,源码里面几乎所有的方法都是HashMap实现的。

在这里插入图片描述

转载地址:http://mnbdi.baihongyu.com/

你可能感兴趣的文章
当前主要目标和工作
查看>>
Intellij IDEA启动优化,让开发的感觉飞起来
查看>>
使用 Springboot 对 Kettle 进行调度开发
查看>>
如何优雅的编程,lombok你怎么这么好用
查看>>
一文看清HBase的使用场景
查看>>
除了负载均衡,Nginx还可以做很多,限流、缓存、黑白名单
查看>>
解析zookeeper的工作流程
查看>>
搞定Java面试中的数据结构问题
查看>>
慢慢欣赏linux make uImage流程
查看>>
linux内核学习(7)脱胎换骨解压缩的内核
查看>>
以太网基础知识
查看>>
慢慢欣赏linux 内核模块引用
查看>>
kprobe学习
查看>>
慢慢欣赏linux phy驱动初始化2
查看>>
慢慢欣赏linux CPU占用率学习
查看>>
2020年终总结
查看>>
linux内核学习(4)建立正式内核的页式内存映射, 以x86 32位模式为例
查看>>
Homebrew指令集
查看>>
React Native(一):搭建开发环境、出Hello World
查看>>
React Native(二):属性、状态
查看>>