点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

来源 | 公众号「武培轩」

Java中Set集合是如何实现添加元素保证不重复的?

Set集合是一个无序的不可以重复的集合。今天来看一下为什么不可以重复。

Set是一个接口,最常用的实现类就是HashSet,今天我们就拿HashSet为例。

先简单介绍一下HashSet类

HashSet类实现了Set接口, 其底层其实是包装了一个HashMap去实现的。HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能。

先看下HashSet的几个构造方法。

// 默认构造函数 底层创建一个HashMappublic HashSet() {// 调用HashMap的默认构造函数,创建mapmap = new HashMap<E,Object>();}// 带集合的构造函数public HashSet(Collection<? extends E> c) {// 创建map。map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));// 将集合(c)中的全部元素添加到HashSet中addAll(c);}// 指定HashSet初始容量和加载因子的构造函数public HashSet(int initialCapacity, float loadFactor) {map = new HashMap<E,Object>(initialCapacity, loadFactor);}// 指定HashSet初始容量的构造函数public HashSet(int initialCapacity) {map = new HashMap<E,Object>(initialCapacity);}HashSet(int initialCapacity, float loadFactor, boolean dummy) {map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);}

再来看HashSet中的声明。

private transient HashMap<E,Object> map;// 用来匹配Map中后面的对象的一个虚拟值
private static final Object PRESENT = new Object();

接下来就是我们的重点HashSet的add()方法,贴上源码。

    /*** 将元素e添加到HashSet中,也就是将元素e作为Key放入HashMap中** @param e 要添加到HashSet中的元素* @return true 如果不包含该元素*/public boolean add(E e) {return map.put(e, PRESENT)==null;}

从源码我们可以看出HashSet的add()方法又调用了HashMap中的put()方法,那我们再跳转到HashMap中的put()方法中。

    public V put(K key, V value) {// 倒数第二个参数false:表示允许旧值替换// 最后一个参数true:表示HashMap不处于创建模式return putVal(hash(key), key, value, false, true);}

HashMap中的put()方法又调用了putVal()方法来实现功能,再看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;//如果哈希表为空,调用resize()创建一个哈希表,并用变量n记录哈希表长度if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;/*** 如果指定参数hash在表中没有对应的桶,即为没有碰撞* Hash函数,(n - 1) & hash 计算key将被放置的槽位* (n - 1) & hash 本质上是hash % n,位运算更快*/if ((p = tab[i = (n - 1) & hash]) == null)//直接将键值对插入到map中即可tab[i] = newNode(hash, key, value, null);else {// 桶中已经存在元素Node<K, V> e;K k;// 比较桶中第一个元素(数组中的结点)的hash值相等,key相等if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))// 将第一个元素赋值给e,用e来记录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 1sttreeifyBin(tab, hash);break;}// 链表节点的<key, value>与put操作<key, value>相同时,不做重复操作,跳出循环if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}// 找到或新建一个key和hashCode与插入元素相等的键值对,进行put操作if (e != null) { // existing mapping for key// 记录e的valueV oldValue = e.value;/*** onlyIfAbsent为false或旧值为null时,允许替换旧值* 否则无需替换*/if (!onlyIfAbsent || oldValue == null)e.value = value;// 访问后回调afterNodeAccess(e);// 返回旧值return oldValue;}}// 更新结构化修改信息++modCount;// 键值对数目超过阈值时,进行rehashif (++size > threshold)resize();// 插入后回调afterNodeInsertion(evict);return null;}

从源码中,我们可以看出将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。如果这个两个key的equals比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。且HashSet中add()中 map.put(e, PRESENT)==null 为false,HashSet添加元素失败。因此,如果向HashSet中添加一个已经存在的元素,新添加的集合元素不会覆盖原来已有的集合元素。

往期推荐

百度开源的 71 个项目,看看你用过几个?

Spring Boot + Dataway :接口不用写,配配就出来?

用Java写一个植物大战僵尸简易版!

GitHub发布重大更新,关系到所有程序员!

GitHub 热榜:文字识别神器,超轻量级中文 OCR!

听说你想从事中间件开发?

扫码登录是如何实现的?

扫一扫,关注我

一起学习,一起进步

Java中Set集合是如何实现添加元素保证不重复的?相关推荐

  1. 万字长文深入理解java中的集合-附PDF下载

    文章目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fai ...

  2. java中的集合框架_JAVA中的集合框架(上)List

    第一节 JAVA中的集合框架概述 集合的概念,现实生活中:很多事物凑在一起就是一个集合:数学中的集合:具有相同属性事物的总体:JAVA中的集合:是一种工具类,就像是容器,储存任意数量的具有共同属性的对 ...

  3. java中各种集合的用法和比较

    一,java中各种集合的关系图 Collection       接口的接口     对象的集合  ├ List           子接口         按进入先后有序保存   可重复  │├ L ...

  4. java 中的集合_JAVA中的集合

    JAVA中集合,是java中的基础概念,今天接着跟讲课准备课件的机会,重新整理一下关于JAVA中的集合的概念. 集合的概念 java中的集合类是用来存放对象的 集合相当于一个容器,里面包容着一组对象- ...

  5. Java中的集合List - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的集合List - 入门篇>,希望对大家有帮助,谢谢 简介 说实话,Java中的集合有很多种,但是这里作为入门级别,先简单介绍第一种 ...

  6. Java中的集合多线程的理解

    Java中的集合 1.List.Set和Queue [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwpgIu9t-1587300088779)(file:///C:\ ...

  7. Java中的集合浅析

    java中的集合就像是一个容器一样,专门用来存储java对象的引用,这些对象可以是任意的数据类型并且长度可变,集合类位于java.util包中. java集合按照其存储结构可以分为两大类,一个是单列集 ...

  8. Java中List集合介绍(炒鸡详细呦)

    Java中List集合介绍 文章目录 Java中List集合介绍 1,Java集合介绍 2,List介绍 2.1 ArrayList集合 2.2 LinkedList集合 3,List常用方法 3.1 ...

  9. java去重复的集合_如何去除Java中List集合中的重复数据

    1.循环list中的所有元素然后删除重复 public class duplicatRemoval { public static List removeDuplicate(List list){ f ...

最新文章

  1. SICP 习题 (2.7) 解题总结 : 定义区间数据结构
  2. 元宇宙教不会区块链赚钱
  3. C++内部类访问外部类
  4. Jzoj5317 Func
  5. PyTorch Softmax
  6. mysql cluster 用户_mysql cluster统一用户权限
  7. 【Machine Learning 一】监督学习与无监督学习
  8. 为什么很多公司只给博士发安家费,却不给研究生和本科生发?
  9. 使用 sync.ErrGroup 实现并发搜索文件
  10. android学习中常见问题集锦
  11. paip.提高效率---集合的存取括号方式 uapi java python php js 的实现比较
  12. java多线程读取分割的文件_java多线程批量读取文件(二)--读写分离
  13. 方舟生存进化掉落物代码
  14. CentOS7或7以上,无iptable解决方法
  15. PS 宏使用方法记录
  16. sof与NIOS II的elf固件合并生成jic文件
  17. 插入式CAN总线隔离适配器LCAN-Opto在高空作业平台上隔离干扰、调理信号的应用案例介绍
  18. php代码审计靶场,RIPS --代码审计靶场(第一关)-华盟网
  19. rewind() 函数
  20. 判断电脑是否安装WPS

热门文章

  1. linux chroot 命令 设置根目录路径
  2. golang beego框架对运行异常的处理
  3. 去掉VS2012中的红色波浪下划线
  4. 直接内存访问(DMA)
  5. QEMU+KVM学习笔记
  6. go网站服务器搭建,使用Go搭建服务器记录(一)
  7. c语言文件操作中换行,关于文件操作,碰到空格就换行
  8. 使用eBPFbcc提取内核网络流量信息(二)
  9. centos 重装 php5,centos6.6学习笔记:安装PHP5.5
  10. html 文字不可选择,有没有办法在HTML页面上使文本无法选择?