Java Map和Set

news2024/11/24 8:47:59

目录

    • 1. 二叉排序树(二叉搜索树)
      • 1.1 二叉搜索树的查找
      • 1.2 二叉搜索树的插入
      • 1.3 二叉搜索树的删除(7种情况)
      • 1.4 二叉搜索树和TreeMap、TreeSet的关系
    • 2. Map和Set的区别与联系
      • 2.1 从接口框架的角度分析
      • 2.2 从存储的模型角度分析【2种模型】
    • 3. 关于Map
      • 3.1 Map的注意事项
      • 3.2 TreeMap 和 HashMap的对比
      • 3.3 Map.Entry<k,v>的用法 (遍历)
    • 4. 关于Set
      • 4.1 Set的注意事项
      • 4.2 TreeSet 和 HashSet的对比
      • 4.3 迭代器遍历Set
    • 5. 哈希表
      • 5.1 哈希函数
      • 5.2 冲突
      • 5.3 冲突的解决:开放地址法和链地址法
      • 5.4 哈希表与Java集合类的关系
    • 6. HashMap源码分析
      • 6.1 成员变量
      • 6.2 构造方法
      • 6.3 put方法源码

1. 二叉排序树(二叉搜索树)

二叉搜索树又称二叉排序树

  • 若左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 左右子树也分别是二叉搜索树
    【左小右大】

1.1 二叉搜索树的查找

【二叉搜索树的查找,一般情况下一次可以干掉很多数据】
为了保证每次可以干掉很多数据
思路:取遍历节点为cur,每次要查找的值val和cur的val进行比较,如果要查找的值比cur的val小,则去cur的左边继续查找,如果大则去cur的右边继续查找。

    public boolean search(int val) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val == val) {
                return true;
            }else if (cur.val > val) {
                cur = cur.left;
            }else {
                cur = cur.right;
            }
        }
        return false;
    }

1.2 二叉搜索树的插入

思路:整体思路与查找的思路类似,但是二叉搜索树的每次插入的节点一定是叶子节点,因此需要记录待插入节点的双亲。
当cur为空时,parent记录了待插入结点的父亲位置,再用val和parent的val比较进行插入。

    public void insert (int val) {
        Node node = new Node(val);
        if (root == null) {
            root = node;
            return;
        }
        Node cur = root;
        Node parent = root;
        while (cur != null) {
            if (cur.val == val) {
                return;
            }else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            }else {
                parent = cur;
                cur = cur.right;
            }
        }
        if (parent.val > val) {
            parent.left = node;
        }else {
            parent.right = node;
        }
    }

1.3 二叉搜索树的删除(7种情况)

设待删除结点为cur,待删除结点的双亲结点为parent
整体看分以下3种大情况:
①cur的左为空
②cur的右为空
③cur的左和右都不为空
继续细分:cur是不是root;cur不是root的话,是parent的左还是parent的右
故有3 + 3 + 1 = 7种情况。

  1. cur.left == null
    ①cur是root
    则 root = cur.right;
    ②cur不是root,cur是parent.left
    则 parent.left = cur.right;
    ③cur不是root,cur是parent.right
    则 parent.right = cur.right;
  2. cur.right == null
    ①cur是root
    则 root = cur.left;
    ②cur不是root,cur是parent.left
    则 parent.left = cur.left;
    ③cur不是root,cur是parent.right
    则 parent.right = cur.left;
  3. cur.left != null && cur.right != null
    此时需要使用替换法进行删除,即在它的右子树种寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

    1.4 二叉搜索树和TreeMap、TreeSet的关系

    TreeMap和TreeSet是Java种利用搜索树实现的Map和Set,实际上用的是红黑树,红黑树是一颗近似平衡的二叉搜索树,即在二叉搜索树的基础之上+颜色以及红黑树性质。

2. Map和Set的区别与联系

2.1 从接口框架的角度分析

在这里插入图片描述
Map是一个独立的接口,而Set继承自Collection接口。
TreeMap 和 TreeSet 都继承了一个Sorted接口,说明 Tree某某 都是经过排序的,即 Tree某某 都是关于key有序的。

2.2 从存储的模型角度分析【2种模型】

  1. Map中存储的是键值对key-value
  2. Set中只存储了key

3. 关于Map

Map是一个接口类,该类没有继承collection,该类中存储的是<k,v>结构的键值对,并且k一定是唯一的,不能重复。

3.1 Map的注意事项

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap。
        Map<Integer, Integer> map1 = new HashMap<>();
        Map<Integer, Integer> map2 = new TreeMap<>();
  1. Map中存放键值对的键key是唯一的,key不可以重复,但是value可以重复。
  2. 在TreeMap中插入键值队的时候,key不能为空,否则会抛出NPE(空指针异常),但是value可以为空,因为TreeMap中的key是要进行比较的;反而HashMap的key和value都可以为空,因为HashMap不涉及比较。
  3. Map中的key可以全部分离出来,存储到Set中进行访问(因为key不能重复,set是天然的去重),Map中的value可以全部分离出来,存储到Collection的任何一个子集合中(value可能有重复)

3.2 TreeMap 和 HashMap的对比

Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2N)O(1)
是否有序关于key有序无序
线程安全不安全不安全
插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址
比较与覆写key必须能够比较,否则会抛出类型转换异常自定义类型需要覆写equals和hashcode方法
应用场景需要key有序的场景下key是否有序不关心,需要更高的时间性能

3.3 Map.Entry<k,v>的用法 (遍历)

Set<Map.Entry <k,v>> entrySet(), 返回所有的key-value映射关系
在这里插入图片描述

        Map<Integer, Integer> map = new TreeMap<>();
        map.put(1,2);
        map.put(2,3);
        map.put(3,6);
        //key一定是可以进行比较的

        for(Map.Entry<Integer,Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

分析:
打印所有的键值对
entrySet():将Map中的键值对放在Set中返回了。
在这里插入图片描述

4. 关于Set

Set与Map主要的不同有2点:
①Set是继承自Collection的接口类
②Set中只存储了Key
Map不能使用迭代器遍历,但是Set可以,因为Set实现了Iterable接口

4.1 Set的注意事项

  1. Set是继承自Collection的一个接口类

  2. Set中只存储了key,并且要求key一定要唯一

  3. Set的底层使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中
    在这里插入图片描述

  4. Set最大的功能就是对集合中的元素进行去重

  5. TreeSet中不能插入null的key,但是HashSet可以

4.2 TreeSet 和 HashSet的对比

Set底层结构TreeSetHashSet
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2N)O(1)
是否有序关于key有序不一定有序
线程安全不安全不安全
插入/删除/查找区别按照红黑树的特性进行插入和删除通过哈希函数计算哈希地址
比较与覆写key必须能够比较,否则会抛出类型转换异常自定义类型需要覆写equals和hashcode方法
应用场景需要key有序的场景下key是否有序不关心,需要更高的时间性能

4.3 迭代器遍历Set

Iterator< E> iterator() 返回迭代器,可以利用其进行遍历

        Set<Integer> set = new TreeSet<>();
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(8);
        set.add(4);
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }

运行结果:
在这里插入图片描述

5. 哈希表

5.1 哈希函数

  1. 哈希表的效率非常高,查找、删除、插入的时间复杂度都是O(1)。
  2. 理想的搜索方法:不经过任何比较,一次直接从表中得到想要搜索的元素,即一一映射关系。
  3. 哈希方法中使用的转换函数称为哈希函数,构造出来的结构称为哈希表(Hash Table)。
  4. 哈希函数计算出来的地址能均匀分布在整个空间中。

5.2 冲突

  1. 冲突: 不同关键字通过相同哈希函数计算出相同的哈希地址
  2. 冲突的发生是必然的,冲突是不可避免的,只能降低冲突率。
  3. 避免冲突:负载因子调节,其值= 填入表中的元素个数/哈希表的长度,因此可以增加哈希表的长度避免冲突。Java的系统库限制了荷载因子为0.75。

5.3 冲突的解决:开放地址法和链地址法

  1. 开放地址法(闭散列)
    ①线性探测
    ②二次探测
    闭散列最大的缺陷:空间利用率比较低
  2. 链地址法(开散列) 数组+链表+红黑树
    思路:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于桶一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
    注意:数组长度>=64,链表长度>=8,就会变成红黑树。

5.4 哈希表与Java集合类的关系

  1. HashMap和HashSet是用哈希表实现的Map和Set。
  2. Java会在冲突链表长度大于一定阈值后,将链表转变成红黑树(搜索树)。
  3. Java中计算哈希值实际上调用的是类的hashcode方法,进行key的相等性比较是调用key的equals方法
  4. 所有自定义类的HashMap的key或者HashSet的值,必须重写hashcode和euqals方法,而且必须做到euqals相等的对象,hashcode一定也是一样的。
  5. 扩容之后要注意每个元素都要重新计算hashcode哈希值。

面试问题:

  1. 问:如果2个对象hashcode一样,equals一定一样吗?
    答:不一定,hashcode一样只能证明我们要找的位置一样,位置一样的下面有很多值,无法确定2个对象的euqals。
  2. 问:如果2个对象equals一样,hashcode一定一样吗?
    答:一定,equals一样则hashcode一定一样。

6. HashMap源码分析

6.1 成员变量

  1. 默认容量:16,最大容量:2的30次方
    在这里插入图片描述
  2. 默认的负载因子:0.75
    在这里插入图片描述
  3. 树化的条件(数组+链表变成红黑树)
    链表长度超过8,数组的容量大于等于64 在这里插入图片描述
  4. 解树的条件(红黑树退化为链表+数组)
    链表的阈值为6的时候
    在这里插入图片描述
  5. table数组的每一个元素是node类型的结点地址
    在这里插入图片描述

6.2 构造方法

  1. 不带参数的构造方法
    负载因子等于默认的负载因子0.75,没有给table数组进行初始化,意味着table数组没有分配内存,数组的大小是0。
    (所以,为什么等会put元素可以put进去?----> 说明在第一次put的时候,会把数组进行初始化为默认容量16)
    在这里插入图片描述
  2. 带2个参数的构造方法
    一个是给定的容量参数,一个是给定的负载因子参数
    在这里插入图片描述
    如果给定的参数容量大于2的30次方,则容量为2的30次方;
    如果给定的参数容量小于0或者给定的负载因子小于0那么就抛异常。
    最后负载因子满足条件直接赋值,但是容量还需要经过tableSizeFor进一步筛选。
    在这里插入图片描述
    tableSizeFor里面:返回最接近目标的一个二次幂整数
    例如:
    传入10:2的3次方=8,2的4次方16,因此返回16。

面试题:调用构造方法给了1000,请问最后哈希数组的长度是多少?
答:1024

在这里插入图片描述

HashMap的最大容量保证是2的n次方,但是如果没有传入一个2的次方怎么办?
答:HashMap会将传入的参数做校验,返回距离传参最近的一个2的n次方的值,例如传入15会初始化为16。

那么,为什么返回二次幂呢?
分析put源码。

6.3 put方法源码

在这里插入图片描述
①先把key给到hash函数中,hash(key),调用hash方法,将引用类型key转换成整数类型
在这里插入图片描述
②hash这个方法中调用了hashcode方法,如果key重写了hashcode方法则会调用自己的hashcode方法,如果没有重写,则会调用Object类的hashcode方法。

h是通过hashcode方法得到的32位的整数,h 异或上 h右移16位
为什么要右移16位?
答:为了更好的均匀分布,低16位和高16位异或,可以让结果更均匀。
在这里插入图片描述

③i = (n - 1) & hash 等价于 n % len,位运算一定是最快的,一定要保证n是2的次幂,这样2个公式才等价。【所以初始化的长度为2的整数次幂】
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QML键盘事件

在QML中&#xff0c;当有一个按键按下或释放时&#xff0c;会产生一个键盘事件&#xff0c;将其传递给获得有焦点的QML项目&#xff08;讲focus属性设置为true&#xff0c;则获得焦点&#xff09;。 按键处理的基本流程&#xff1a; Qt接收密钥操作并生成密钥事件。如果 QQuic…

keepalived学习记录:对其vip漂移过程采用gdb跟踪

对其vip漂移过程采用gdb跟踪keepalived工具主要功能产生vip漂移过程两种情况gdb调试常用命令gdb调试时打到的函数栈&#xff08;供学习参考&#xff09;函数栈的图是本人理解下画的&#xff0c;不对请多指正 keepalived主要有三个进程&#xff0c;父进程是core进程&#xff0c;…

python编程:查找某个文件夹下所有的文件,包括子文件加下的所有文件,读取指定类型的文件

目录 一、实现要求 二、代码实现 三、效果测试 一、实现要求 1、在电脑上有一个文件夹&#xff0c;该文件夹下面还有子文件夹&#xff0c;具体层级不清楚&#xff0c;需要实现将该文件夹下所有的文件路径读取出来&#xff1b; 2、在1的基础上&#xff0c;只需读取指定类型的文…

论文复现-2:代码部分

以CONLL03数据集为例 文章目录1 整体框架2 数据结构2.1 原始数据集2.2 处理之后的数据集3 代码部分3.0 模型参数3.1 数据预处理3.2 模型方法3.1.1 定义表示的学习权重项的学习双塔模型3.2.2 forward3.3 损失函数3.4 训练与推理Ablation study训练实例1 整体框架 任务是实体识别…

MVVM模式下如何正确【视图绑定+数据】

概述 我如何&#xff08;不在后面的代码中使用代码&#xff09;自动绑定到我想要的视图&#xff1f;据我了解&#xff0c;如果正确完成&#xff0c;这就是模式应该如何工作。我可以使用主窗口 xaml 中的代码实现这一切&#xff0c;我甚至正确创建了一个资源字典&#xff08;因…

python Pytest生成alluer测试报告的完整教程

1.下载allure包到本地&#xff0c;解压 网上很多资料&#xff0c;这边不提供了 2.配置环境变量 将上面解压后bin文件的路径复制&#xff0c;添加到环境变量Path下 3.验证环境变量配置是否功 在cmd中输入allure&#xff0c;回车 。查看allure是否成功&#xff1a; 4.pyc…

Spring Boot 整合Redis使用Lua脚本实现限流

目录一、简介二、maven依赖三、编码实现3.1、配置文件3.2、配置类3.3、注解类3.4、切面类3.5、lua脚本3.6、自定义异常和全局异常3.7、控制层四、验证4.1、单用户限流4.2、接口限流结语一、简介 本篇文章主要来讲Spring Boot 整合Redis使用Lua脚本实现限流&#xff0c;实现限流…

Lsof命令介绍

LSOF&#xff08;List Open Files&#xff09;是一款功能强大的开源工具&#xff0c;用于列出当前系统上打开的文件和进程。该工具可以帮助系统管理员和开发人员快速查找正在使用某个文件的进程&#xff0c;以及在系统上使用磁盘空间最多的进程。 本文将介绍LSOF的基本用法和常…

计算机网络自检

1 计网体系结构 因特网结构&#xff1a; 计网三个组成成分&#xff1a; 工作方式-其中2个部分&#xff1a; 功能-两个子网&#xff1a; 5个XAN分别是&#xff1a; 传输技术&#xff0c;两者的主要区别&#xff1a; 4种基本网络拓扑结构&#xff1a; 3种交换技术&#xff1a; 协…

南卡NEO骨传导首发新机,超前无线充设计,树立行业标杆!!!

​3月2号&#xff0c;更专业的骨传导运动耳机——南卡&#xff0c;发布了以轻运动为全新方向系列的南卡NEO&#xff0c;通过迭代升级的声学技术进一步的优化了音质&#xff0c;打造更强一代的音质体验。 音质全新升级&#xff0c;分”響“全新体验 一直以来南卡专业的实验团队…

计算机网络第5章(运输层)学习笔记

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

linux环境创建anaconda虚拟环境安装tensorflow-gpu版本

linux环境创建anaconda虚拟环境安装tensorflow-gpu版本1.找到相应版本2.下载步骤2.1选择下载版本2.2 创建虚拟环境2.3 进入虚拟环境2.5 更新三个包2.6 安装tensorflow和keras2.7 验证是否安装成功2.8 检验GPU是否可用2.9 测试代码3.成功&#xff0c;终于成功了&#xff01;&…

spring-boot rabbitmq整合

文章请参考&#xff1a;Springboot 整合RabbitMq &#xff0c;用心看完这一篇就够了 mven依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></depende…

张大大直播爆火?跨境电商直播能从中学到什么

最近这个张大大的直播间确实很火哈&#xff0c;连龙哥这个不怎么看娱乐新闻的也经常刷到。龙哥专门去看了几次张大大的这个直播&#xff0c;确实有点东西。每场都可以做到这么高流量着实是不容易。龙哥就凭借自己一些电商直播经验&#xff0c;给大家总结一下&#xff0c;我们可…

年薪20W软件测试工程师必备的6大技能(建议收藏)

软件测试 随着软件开发行业的日益发展&#xff0c;岗位需求量和行业薪资都不断增长&#xff0c;想要入行的人也是越来越多&#xff0c;但不知道从哪里下手&#xff0c;今天&#xff0c;就给大家分享一下&#xff0c;软件测试行业都有哪些必会的方法和技术知识点&#xff0c;作…

Java知识补充

ArrayList 继承结构 为什么要先继承AbstractList&#xff0c;而让AbstractList先实现List&#xff1f;而不是让ArrayList直接实现List&#xff1f; 这里是有一个思想&#xff0c;接口中全都是抽象的方法&#xff0c;而抽象类中可以有抽象方法&#xff0c;还可以有具体的实现方…

无聊小知识.03 Springboot starter配置自动提示

1、前言Springboot项目配置properties或yaml文件时候&#xff0c;会有很多spring相关的配置提示。这个是如何实现的&#xff1f;如果我们自己的配置属性&#xff0c;能否也自动提示&#xff1f;2、Springboot配置自动提示其实IDE是通过读取配置信息的元数据而实现自动提示的。S…

HCIE-Cloud Computing LAB备考第二步:逐题攻破--第一题:FusionCompute--合并小节

FusionCompute 三大要求&#xff1a;扩容对接共享存储windows版本的FC操作 FCWindows题目思维导图 扩容一台CNA节点&#xff0c;配置管理地址设置为&#xff1a;192.168.100.212。密码设置为&#xff1a;Cloud12#$。主机名设置为CNA2。【安装CNA 】【ctrlaltdel之后按esc选择…

双向链表+循环链表

循环链表双向链表 循环链表 循环链表是头尾相接的链表(即表中最后一个结点的指针域指向头结点&#xff0c;整个链表形成一个环)(circular linked list) **优点&#xff1a;**从表中任一结点出发均可访问全部结点 循环链表与单链表的主要差别当链表遍历时&#xff0c;判别当前…

精准测试对于覆盖率技术的全新诠释

对于白盒测试有深入研究的技术人员可能会问到&#xff0c;精准测试还是很多用到了覆盖率技术&#xff0c;这些本来不就是有开源的工具吗?下面我们来比较一下&#xff1a; 开源的覆盖率工具&#xff1a; 1、 将所有的测试产生的覆盖率混在一起&#xff0c;不具备快速定位缺陷与…