[数据结构]HashSet与LinkedHashSet的底层原理学习心得

我们区分list和set集合的标准是三个:有无顺序,可否重复,有无索引。
list的答案是:有顺序,可重复,有索引。这也就是ArrayList和LinkedList的共性
set的答案是:顺序内部再区分,不可以重复,无索引
我们接下来可以通过顺序的标准在set集合中进行再区分:
1.HashSet无顺序
2.LinkedHashSet有顺序
3.TreeSet可排序
Hashset的底层是哈希表->一种对于增删改查数据性能较好的数据结构
分析哈希表的构成:
Before JDK8:数组+链表
After JDK8:数组+链表+红黑树
哈希值:哈希表的灵魂所在->对象的整数表现形式
我们可以使用Object类中的hashcode方法来计算某个对象,运算结果是int类型的整数
问题来了:我们会拿对象的什么来进行计算?答案是:地址值
举个简单的例子:
现在有一个对象的地址值是0x0011 我通过这个地址值算出来一个哈希值:6794651616
然后我们在哈希表内添加元素的时候,注意,是在有索引的哈希表内添加元素的时候
这里我们避免一个认识误区:Hashset是没有索引的,但是构成Hashset的底层数据结构-哈希表是有索引的
我们将添加的元素在哈希表上的索引index的公式给出:
int index=(数组长度-1)&哈希值; 这里又证明了先前我们提出的哈希表底层原理:数组+链表+红黑树(JDK8以前)
那么此时我们会有一个问题:在Object类下的hashcode和复写后的hashcode产生的哈希值是一样的么?
答案是不一样的->
如果没有重写hashcode方法:不同对象计算出的哈希值是不同的
如果已经重写hashcode方法:不同的对象只要属性值相同,计算出的哈希值是一样的
这句话有点晦涩难懂,我们举个例子:
我需要构建一个学生信息管理系统,需要添加两个学生的名字进去 两个"小明"
如果我使用了object的hashcode 那么计算出的哈希值两次是不一样的 于是两个小明会被分配到不同的空间
但是如果我使用了重写后的hashcode 那么计算出的哈希值两次都是一样的 小明都会被储存到同一块区域当中去
但是注意:在小部分的情况下 不同的属性值或者不同的属性值计算机出的哈希值是一样的
这样的情况就是:哈希碰撞。因为int的范围是-21E到+21E 但是我现在创建50E个对象
一点有8E对象的哈希值是一样的 这样就会发生哈希碰撞 但是这样的极端概率是不高的
1.首先创建一个默认长度为16 默认加载因子是0.75的数组 数组名叫table
2.根据元素的哈希值跟数组的长度进行计算 计算出当前元素应存入的位置
公式: int index= (数组元素-1)&哈希值; 注意&是按位与
3.判断当前这个index对应的位置是否是Null 如果是null直接存入
4.如果当前位置不是Null 就说明已经有元素了 调用equals方法来比较属性值
例子:比如我要在Index为4的位置存入一个新的数据 但是4位置已经有数据了
5.如果属性值是一样:不存
  属性值不一样:存入数组,形成链表(JDK8以前 新的元素存入数组 老的元素挂在新元素下面)
                                (JDK8以后 新元素挂在老元素下面)
 加载因子是什么:数组的扩容时机:
 16x0.75=12元素满时添加
 6.红黑树的出现:我们先前谈到了在哈希表的相同位置添加元素会触发equals方法
 然后新的数据会挂载在哈希表的下面,其中红黑树就是一种jdk8以后产生的新挂载方法
 JDK8满足条件:a.链表长度超过8 b.数组长度>=64
 7.集合中储存的是自定义对象,必须要重写hashcode和equals方法
 前者的目的是为了属性值取代地址值,后者的目的是用属性值去进行比较
 我们构造链表和树的目的就是为了减少哈希碰撞

 Hashset为什么没有索引 Hash表上挂着许多的链表和红黑树 无法准确的表示具体的索引 因为一个index上可以挂着红黑树和链表 你如何确定二者的索引呢?

 学习问答题:
 Q1:Hashset的集合的底层数据结构是什么样的
 答案:HashTable(散列表)在JDK8以前 HashTable由数组和链表构成的 在JDK8以后由数组 链表 红黑树构成
 一个HashTable的所谓索引也就是Hashbucket(哈希桶),每一个哈希桶上可以挂载链表和红黑树
 Q2:Hashset添加元素的过程是如何的
 答案:如果哈希值对应的Hashbucket为Null 直接添加 else 再调用equals进行判断 一样就不存 不一样就存

 Q3 Hashset为什么取和存的顺序不一样
 答案:我遍历一个hashset其实是从hashtable的序号为0的hashbucket开始的 但是我存入一个元素到hashbucket是根据hashcode算出来的
 读取和存入执行的过程是不一样的
 Q4:HashSet为什么没有索引:
 答案:使用到了链表和红黑树挂在hashbucket上面 过于复杂无法用索引表示
 Q5:HashSet是用什么机制去保证去重复的?
 答案:重写后的equals方法

————————————————————————————————————————————
LinkedHashSet底层原理
有序,不重复,无索引
底层数据结构:哈希表+链表
import java.util.*;

public class Main{
    public static void main(String[] args){
 /*
 我们区分list和set集合的标准是三个:有无顺序,可否重复,有无索引。
 list的答案是:有顺序,可重复,有索引。这也就是ArrayList和LinkedList的共性
 set的答案是:顺序内部再区分,不可以重复,无索引
 我们接下来可以通过顺序的标准在set集合中进行再区分:
 1.HashSet无顺序
 2.LinkedHashSet有顺序
 3.TreeSet可排序
  */
        Set<String> Hash = new HashSet<>();
        Set<String> LinkedHash = new LinkedHashSet<>();
        Set<String> TreeSet = new TreeSet<>();
        Hash.add("张三");
        Hash.add("李四");
        Hash.add("王五");
        //1.ForEach+lambda表达式遍历
        Hash.forEach(s->System.out.println(s));
        //2.加强for循环遍历
        for(String s:Hash){
            System.out.println(s);
        }
        //3.迭代器遍历
        Iterator<String> it = Hash.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        //现在我们开始系统学习set,第一课:Hashset
        /*
        Hashset的底层是哈希表->一种对于增删改查数据性能较好的数据结构
        分析哈希表的构成:
        Before JDK8:数组+链表
        After JDK8:数组+链表+红黑树
        哈希值:哈希表的灵魂所在->对象的整数表现形式
        我们可以使用Object类中的hashcode方法来计算某个对象,运算结果是int类型的整数
        问题来了:我们会拿对象的什么来进行计算?答案是:地址值
        举个简单的例子:
        现在有一个对象的地址值是0x0011 我通过这个地址值算出来一个哈希值:6794651616
        然后我们在哈希表内添加元素的时候,注意,是在有索引的哈希表内添加元素的时候
        这里我们避免一个认识误区:Hashset是没有索引的,但是构成Hashset的底层数据结构-哈希表是有索引的
        我们将添加的元素在哈希表上的索引index的公式给出:
        int index=(数组长度-1)&哈希值; 这里又证明了先前我们提出的哈希表底层原理:数组+链表+红黑树(JDK8以前)
        那么此时我们会有一个问题:在Object类下的hashcode和复写后的hashcode产生的哈希值是一样的么?
        答案是不一样的->
        如果没有重写hashcode方法:不同对象计算出的哈希值是不同的
        如果已经重写hashcode方法:不同的对象只要属性值相同,计算出的哈希值是一样的
        这句话有点晦涩难懂,我们举个例子:
        我需要构建一个学生信息管理系统,需要添加两个学生的名字进去 两个"小明"
        如果我使用了object的hashcode 那么计算出的哈希值两次是不一样的 于是两个小明会被分配到不同的空间
        但是如果我使用了重写后的hashcode 那么计算出的哈希值两次都是一样的 小明都会被储存到同一块区域当中去
        但是注意:在小部分的情况下 不同的属性值或者不同的属性值计算机出的哈希值是一样的
        这样的情况就是:哈希碰撞。因为int的范围是-21E到+21E 但是我现在创建50E个对象
        一点有8E对象的哈希值是一样的 这样就会发生哈希碰撞 但是这样的极端概率是不高的
         */

        //1.创建一个对象
        Student s1=new Student("小明",23);
        Student s2=new Student("小明",23);

        //2.如果没有重写hashcode s1和s2返回的哈希值是不一样的
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        //你会发现在Student重写hashcode后的返回哈希值是一样的
        //这里的重写我们用alt+insert的便捷键来进行重写

        //哈希碰撞的特例:
        System.out.println("abc".hashCode());
        System.out.println("acD".hashCode());

        //Hashcode在JDK8以前的底层原理
        /*
        1.首先创建一个默认长度为16 默认加载因子是0.75的数组 数组名叫table
        2.根据元素的哈希值跟数组的长度进行计算 计算出当前元素应存入的位置
        公式: int index= (数组元素-1)&哈希值; 注意&是按位与
        3.判断当前这个index对应的位置是否是Null 如果是null直接存入
        4.如果当前位置不是Null 就说明已经有元素了 调用equals方法来比较属性值
        例子:比如我要在Index为4的位置存入一个新的数据 但是4位置已经有数据了
        5.如果属性值是一样:不存
          属性值不一样:存入数组,形成链表(JDK8以前 新的元素存入数组 老的元素挂在新元素下面)
                                        (JDK8以后 新元素挂在老元素下面)
         加载因子是什么:数组的扩容时机:
         16x0.75=12元素满时添加
         6.红黑树的出现:我们先前谈到了在哈希表的相同位置添加元素会触发equals方法
         然后新的数据会挂载在哈希表的下面,其中红黑树就是一种jdk8以后产生的新挂载方法
         JDK8满足条件:a.链表长度超过8 b.数组长度>=64
         7.集合中储存的是自定义对象,必须要重写hashcode和equals方法
         前者的目的是为了属性值取代地址值,后者的目的是用属性值去进行比较
         我们构造链表和树的目的就是为了减少哈希碰撞

         Hashset为什么没有索引 Hash表上挂着许多的链表和红黑树 无法准确的表示具体的索引 因为一个index上可以挂着红黑树和链表 你如何确定二者的索引呢?

         学习问答题:
         Q1:Hashset的集合的底层数据结构是什么样的
         答案:HashTable(散列表)在JDK8以前 HashTable由数组和链表构成的 在JDK8以后由数组 链表 红黑树构成
         一个HashTable的所谓索引也就是Hashbucket(哈希桶),每一个哈希桶上可以挂载链表和红黑树
         Q2:Hashset添加元素的过程是如何的
         答案:如果哈希值对应的Hashbucket为Null 直接添加 else 再调用equals进行判断 一样就不存 不一样就存

         Q3 Hashset为什么取和存的顺序不一样
         答案:我遍历一个hashset其实是从hashtable的序号为0的hashbucket开始的 但是我存入一个元素到hashbucket是根据hashcode算出来的
         读取和存入执行的过程是不一样的
         Q4:HashSet为什么没有索引:
         答案:使用到了链表和红黑树挂在hashbucket上面 过于复杂无法用索引表示
         Q5:HashSet是用什么机制去保证去重复的?
         答案:重写后的equals方法

        ————————————————————————————————————————————
        LinkedHashSet底层原理
        有序,不重复,无索引
        底层数据结构:哈希表+链表






         */

    }
}
 1.首先创建一个默认长度为16 默认加载因子是0.75的数组 数组名叫table
        2.根据元素的哈希值跟数组的长度进行计算 计算出当前元素应存入的位置
        公式: int index= (数组元素-1)&哈希值; 注意&是按位与
        3.判断当前这个index对应的位置是否是Null 如果是null直接存入
        4.如果当前位置不是Null 就说明已经有元素了 调用equals方法来比较属性值
        例子:比如我要在Index为4的位置存入一个新的数据 但是4位置已经有数据了
        5.如果属性值是一样:不存
          属性值不一样:存入数组,形成链表(JDK8以前 新的元素存入数组 老的元素挂在新元素下面)
                                        (JDK8以后 新元素挂在老元素下面)
         加载因子是什么:数组的扩容时机:
         16x0.75=12元素满时添加
         6.红黑树的出现:我们先前谈到了在哈希表的相同位置添加元素会触发equals方法
         然后新的数据会挂载在哈希表的下面,其中红黑树就是一种jdk8以后产生的新挂载方法
         JDK8满足条件:a.链表长度超过8 b.数组长度>=64
         7.集合中储存的是自定义对象,必须要重写hashcode和equals方法
         前者的目的是为了属性值取代地址值,后者的目的是用属性值去进行比较
         我们构造链表和树的目的就是为了减少哈希碰撞

         Hashset为什么没有索引 Hash表上挂着许多的链表和红黑树 无法准确的表示具体的索引 因为一个index上可以挂着红黑树和链表 你如何确定二者的索引呢?

         学习问答题:
         Q1:Hashset的集合的底层数据结构是什么样的
         答案:HashTable(散列表)在JDK8以前 HashTable由数组和链表构成的 在JDK8以后由数组 链表 红黑树构成
         一个HashTable的所谓索引也就是Hashbucket(哈希桶),每一个哈希桶上可以挂载链表和红黑树
         Q2:Hashset添加元素的过程是如何的
         答案:如果哈希值对应的Hashbucket为Null 直接添加 else 再调用equals进行判断 一样就不存 不一样就存

         Q3 Hashset为什么取和存的顺序不一样
         答案:我遍历一个hashset其实是从hashtable的序号为0的hashbucket开始的 但是我存入一个元素到hashbucket是根据hashcode算出来的
         读取和存入执行的过程是不一样的
         Q4:HashSet为什么没有索引:
         答案:使用到了链表和红黑树挂在hashbucket上面 过于复杂无法用索引表示
         Q5:HashSet是用什么机制去保证去重复的?
         答案:重写后的equals方法

        ————————————————————————————————————————————
        LinkedHashSet底层原理
        有序,不重复,无索引
        底层数据结构:哈希表+链表
import java.util.Objects;

public class Student
{
    private String name;
    private int age;

    public Student() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
 @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享几个国内免费使用的 gpt 网站

可放心阅读点击&#xff0c;无邀请链接、邀请码等 今天主要分享几个个免费的GPT网站。 1、思默问答&#xff08;SiteSMO&#xff09; AI写作生成器_智能写作_问答助手 - 思默问答 算是国内比较早的AI应用网站&#xff0c;支持问答&#xff0c;画图等&#xff0c;所有的问答…

visual Studio MFC 平台实现图像增强中的线性变换(负变换)和非线性变换(对数与幂律)

MFC 实现数字图像处理中的图像增强操作 本文使用visual Studio MFC 平台实现图像增强中典型的三种图像增强的方法中的两大类&#xff0c;包括线性变换–>负变换&#xff0c;非线性变换–>对数变换和幂律变换&#xff1b;其中第三大类分段式变换可以参考MFC实现图像增强–…

Presto基础学习--学习笔记

1&#xff0c;Presto背景 2011年&#xff0c;FaceBook的数据仓库存储在少量大型hadoop/hdfs集群&#xff0c;在这之前&#xff0c;FaceBook的科学家和分析师一直靠hive进行数据分析&#xff0c;但hive使用MR作为底层计算框架&#xff0c;是专为批处理设计的&#xff0c;但是随…

孩子都能学会的FPGA:第十九课——FPGA实现流水线操作

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

Rust国内sparse镜像源配置

文章目录 1. 遇到问题1.1 问题现象1.2 解决办法 2. 重新设置最新 sparse源3. 更多参考资料3.1 字节源3.2 ustc 源3.3 清华源3.4 其他人的总结 1. 遇到问题 有好一阵子没有更新源和安装软件了&#xff0c; 使用ustc的源&#xff0c; 更新了好一阵子&#xff0c; 最后安装居然还出…

养身馆推拿会员管理系统,佳易王推拿会员管理软件短信设置教程

养身馆推拿会员管理系统&#xff0c;佳易王推拿会员管理软件短信设置教程 一、佳易王会员管理软件大众版 部分功能简介&#xff1a; 1、会员信息登记 &#xff1a;可以直接使用手机号登记&#xff0c;也可以使用实体卡片&#xff0c;推荐用手机号即可。 2、会员卡类型 &…

压缩docker在主机的虚拟磁盘容量

我们在windows里使用docker时会发现&#xff0c;即使我们已经删除了无用的镜像和容器&#xff0c;主机里挂在docker虚拟磁盘的那个盘&#xff0c;可用空间也没有增加&#xff0c;这是因为虚拟磁盘不会自动缩小&#xff0c;这里我分享一个可用的解决方案。 1.先通过docker回收空…

大小堆的实现(C语言)

目录 前言 一种完全二叉树&#xff1a;堆 堆的概念 堆的性质 建堆的时间复杂度 建堆的空间复杂度&#xff1a; 小堆的实现 必要补充 堆的初始化 堆的销毁 向上调整算法 堆的插入 向下调整算法 堆的删除 获取堆顶元素 获取堆中元素个数 堆的判空 最终代码 He…

保育员个人简历精选7篇

想要在保育员职位的求职过程中脱颖而出吗&#xff0c;参考这7篇精选的保育员简历案例&#xff01;无论您的经验如何&#xff0c;都能找到适合自己的简历样式及参考内容。 保育员个人简历模板下载&#xff08;可在线编辑制作&#xff09;&#xff1a;来幻主简历&#xff0c;做好…

免费HTTPS证书

什么是HTTPS呢&#xff1f;HTTPS全称为Hyper Text Transfer Protocol Secure&#xff0c;即超文本传输安全协议。它是在HTTP的基础上加入了SSL/TLS协议&#xff0c;可以对传输的数据进行加密&#xff0c;有效防止数据被第三方截取或篡改&#xff0c;从而保障了用户的信息安全。…

Docker Compose简单入门

Docker Compose 简介 Docker Compose 是一个编排多容器发布式部署的工具&#xff0c;提供命令集管理容器化应用的完整开发周期&#xff0c;包括服务构建&#xff0c;启动和停止。 Docker Compose 真正的作用是在一个文件&#xff08;docker-compose.yml&#xff09;中定义并运…

Fiddler抓包工具之fiddler设置抓HTTPS的请求证书安装

设置抓HTTPS的请求包 基础配置&#xff1a; 路径&#xff1a;启动Fiddler 》Tools》Options》HTTPS 注意&#xff1a;Option更改完配置需重启Fiddler才能生效 选中"Decrpt HTTPS traffic", Fiddler就可以截获HTTPS请求&#xff0c;如果是第一次会弹出证书安装提…

JS构造函数

构造函数是一种特殊的函数&#xff0c;主要用来初始化对象 使用场景&#xff1a;比如我对象与其他对象都相似&#xff0c;此时可以通过构造函数来快速创建多个类似的对象。 举个例子&#xff1a; // 大头儿子const Son {name:"大头儿子",age:6,gender:"男"…

C++基础 -33- 单目运算符重载

单目运算符重载格式 a和a通过形参确定 data1 operator() {this->a;return *this; }data1 operator(int) {data1 temp*this;this->a;return temp; }举例使用单目运算符重载 #include "iostream"using namespace std;class data1 {public :int a;data1(int…

maven篇---第一篇

系列文章目录 文章目录 系列文章目录前言一、什么是maven?二、Maven能为我们解决什么问题?三、说说maven有什么优缺点?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码…

react native 环境准备

一、必备安装 1、安装node 注意 Node 的版本应大于等于 16&#xff0c;安装完 Node 后建议设置 npm 镜像&#xff08;淘宝源&#xff09;以加速后面的过程&#xff08;或使用科学上网工具&#xff09;。 node下载地址&#xff1a;Download | Node.js设置淘宝源 npm config s…

qnx修改tcp和udp缓冲区默认大小

拷贝/home/test/qnx/qos223/target/qnx7/aarch64le/sbin/sysctl进系统中 https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/s/sysctl.html kern.sbmax 默认262144&#xff0c;这个限制住了发送、接收缓冲器大小 ./sysctl -w kern.sbmax10000…

免费AI洗稿软件【2023最新】

很多时候我们需要通过文字来表达观点、推广产品或服务。然而&#xff0c;长时间的文稿创作不仅费时费力&#xff0c;还容易陷入表达瓶颈。许多写手和从业者纷纷寻找一款方便、高效的AI洗稿工具。 文心一言洗稿软件。这款软件以其独特的文风生成和洗稿功能而备受瞩目。用户只需…

【Qt开发流程】之事件系统3:键盘事件

序章 以下链接是拖放事件介绍和使用示例&#xff1a; 【Qt开发流程】之拖放操作1:介绍链接: https://blog.csdn.net/MrHHHHHH/article/details/134626484 【Qt开发流程】之拖放操作2:使用链接: https://blog.csdn.net/MrHHHHHH/article/details/134632006 以下链接是事件系统…

页面表格高度自适应

前言 现在后端管理系统主页面基本都是由三部分组成 查询条件&#xff0c;高度不固定&#xff0c;可能有的页面查询条件多&#xff0c;有的少表格&#xff0c;高度不固定&#xff0c;占据页面剩余高度分页&#xff0c;高度固定 这三部分加起来肯定是占满全屏的&#xff0c;那么我…
最新文章