Java基础关键_021_集合(五)

news2025/3/16 18:07:06

目  录

一、HashMap

1.key 存储自定义类型

2.Hash 表存储原理

 3.重写 hashCode 和 equals 方法

4.key 为 null 

5.jdk 8 后新特性

(1)初始化时

(2)插入

(3)数据结构

6.容量 

 二、LinkedHashMap

1.说明

2.实例 

三、Hashtable

1.说明

2.实例

3.特有方法

四、Properties

1.说明

2.实例


一、HashMap

1.key 存储自定义类型

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    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);
    }
}
public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Student, Integer> hashMap = new HashMap<>();
        Student s1 = new Student("瑶", 18);
        Student s2 = new Student("刘禅", 19);
        Student s3 = new Student("朵莉亚", 20);
        Student s4 = new Student("蔡文姬", 21);
        Student s5 = new Student("钟馗", 22);
        Student s6 = new Student("钟馗", 22);

        System.out.println(s5.equals(s6));  // true

        System.out.println(s5.hashCode());  // 990368553
        System.out.println(s6.hashCode());  // 1096979270
        System.out.println(s5.hashCode() % 16); // 9
        System.out.println(s6.hashCode() % 16); // 6

        hashMap.put(s1, 1);
        hashMap.put(s2, 2);
        hashMap.put(s3, 3);
        hashMap.put(s4, 4);
        hashMap.put(s5, 5);
        hashMap.put(s6, 6);

        Set<Map.Entry<Student, Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Student, Integer> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        /*
        * Student{name='钟馗', age=22}:6
        * Student{name='刘禅', age=19}:2
        * Student{name='蔡文姬', age=21}:4
        * Student{name='朵莉亚', age=20}:3
        * Student{name='瑶', age=18}:1
        * Student{name='钟馗', age=22}:5
        * */
    }
}

        思考:为什么重写了 equals 方法,证明 5,6 两个 key 相同,但仍然会存储呢?


2.Hash 表存储原理

  1. 先调用 key 的 hashCode 方法,生成哈希值;
  2. 将哈希值对数组长度进行取模运算,即【 哈希值 % 数组长度 】,计算出对应的索引值;
  3. 如果索引处没有存储元素,则将键值对封装为 Node 对象,然后存入该位置中;
  4. 如果索引处有元素,则遍历整个单链表。若遍历出节点的 key 与添加键值对的 key 相同,则做覆盖操作;若遍历出无 key 相同,则把添加的键值对封装成 Node 对象,然后插入单链表的末尾;
  5. 产生哈希冲突的情况:
    1. 不同的 key 获得相同的哈希值;
    2. 通过 key 得到不同的哈希值,但是通过【 哈希值 % 数组长度 】得到的结果相同。
  1. 一个好的哈希函数,是散列分布均匀的;
  2. 解决哈希冲突:将冲突的结点挂在同一链表上或同一红黑树上。 

 3.重写 hashCode 和 equals 方法

        对于 1 中的思考,应该从以下方面考虑。

        如果调用 equals 方法,结果是 true,说明两个对象相同,但仍然存储说明没有发生哈希碰撞。由 2 的 Hash 表存储原理可知,要发生哈希碰撞有两种情况,此时应该要两个相同 key 生成的哈希值相同,才能避免重复存储。所以需要同时重写 hashCode 方法和 equals 方法。

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    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);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Student, Integer> hashMap = new HashMap<>();
        Student s1 = new Student("瑶", 18);
        Student s2 = new Student("刘禅", 19);
        Student s3 = new Student("朵莉亚", 20);
        Student s4 = new Student("蔡文姬", 21);
        Student s5 = new Student("钟馗", 22);
        Student s6 = new Student("钟馗", 22);

        System.out.println(s5.equals(s6));  // true

        System.out.println(s5.hashCode());  // 37783039
        System.out.println(s6.hashCode());  // 37783039
        System.out.println(s5.hashCode() % 16); // 15
        System.out.println(s6.hashCode() % 16); // 15

        hashMap.put(s1, 1);
        hashMap.put(s2, 2);
        hashMap.put(s3, 3);
        hashMap.put(s4, 4);
        hashMap.put(s5, 5);
        hashMap.put(s6, 6);

        Set<Map.Entry<Student, Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Student, Integer> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        /*
         * Student{name='瑶', age=18}:1
         * Student{name='刘禅', age=19}:2
         * Student{name='蔡文姬', age=21}:4
         * Student{name='朵莉亚', age=20}:3
         * Student{name='钟馗', age=22}:6
         * */
    }
}

4.key 为 null 

        key 可以为 null,但只能存在一个,多者会被覆盖。

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Integer, String> hashMap = new HashMap<>();
        hashMap.put(null,"齐");
        hashMap.put(1,"楚");
        hashMap.put(2,"秦");
        hashMap.put(3,"燕");
        hashMap.put(4,"赵");
        hashMap.put(5,"魏");
        hashMap.put(null,"韩");
        System.out.println(hashMap);    // {null=韩, 1=楚, 2=秦, 3=燕, 4=赵, 5=魏}
    }
}

5.jdk 8 后新特性

(1)初始化时

  1. jdk 8 之前,构造方法执行时初始化 table 数组;
  2. jdk 8 之后,第一次调用 put 方法时初始化 table 数组。

(2)插入

  1. jdk 8 之前,头插法;
  2. jdk 8 之后,尾插法。

(3)数据结构

  1. jdk 8 之前,是数组与单向链表的结合;

  2.  jdk 8 之后,是数组、单向链表和红黑树的结合;

  3. 最开始使用单向链表解决哈希冲突。若 结点数 >= 8 且 table 长度 >= 64,则单向链表转换为红黑树;

  4. 当删除红黑树上的结点时,当 结点数 <= 6 时,红黑树转换为单向链表。


6.容量 

  1. 默认情况下,数组长度为 16;
  2. HashMap 容量永远是 2 的次幂,例如:2、4、8、16、32、64……。原因有两点:为了提高哈希计算效率和减少哈希冲突,让散列分布更均匀;
  3. 哈希表中元素越来越多时,因为数组长度固定,所以散列碰撞的几率也随之增大,从而导致单链表过长,降低了哈希表的性能;
  4. 当执行 put 操作时,如果 HashMap 中存储元素的个数超过【数组长度 * 负载因子】的结果(负载因子即 loadFactor,默认值一般为 0.75 ),则需要扩容;
  5. 扩容即把数组大小扩大一倍,然后遍历哈希表中元素,将其重新均匀分散;
  6. 扩容是一个非常消耗性能的操作,所以建议预测需要存储元素的个数;
  7. 例如:设置哈希表的容量为 15,实际创建完 HashMap对象后,实际容量是 12 。因为 HashMap 的容量永远为 2 的次幂,最接近 15 的是 16,16 * 0.75 = 12 。

 二、LinkedHashMap

1.说明

  1. 是 HashMap 的子类,两者用法基本一致;
  2. 但 LinkedHashMap 是有序不可重复的(插入顺序与读取顺序一致且不可重复);
  3. 通过双向链表记录来保证插入顺序;
  4. 效率较 HashMap 低一些;
  5. 其 key 也需要同时重写 equals 和 hashCode 方法;
  6. 底层是哈希表与双向链表结合的数据结构;
  7. key 可以为 null,但只能存在一个,多者会被覆盖。

2.实例 

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put(1, "小禾");
        linkedHashMap.put(2, "小明");
        linkedHashMap.put(3, "小红");
        linkedHashMap.put(4, "小雯");
        linkedHashMap.put(5, "小凡");
        linkedHashMap.put(5, "小州");

        Set<Map.Entry<Integer, String>> entries = linkedHashMap.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        /*
        * 1:小禾
        * 2:小明
        * 3:小红
        * 4:小雯
        * 5:小州
        * */
    }
}

三、Hashtable

1.说明

  1. 和 HashMap 一样,底层也是哈希表;
  2. Hashtable 是线程安全的,方法上都有 synchronized 关键字;
  3. 初始化容量是 11,默认负载因子是 0.75;
  4. 扩容后的容量是原容量的 2 倍;
  5. key 和 value 都不能是 null 。

2.实例

public class HashtableTest {
    public static void main(String[] args) {
        Hashtable<Integer, String> hashtable = new Hashtable<>();
        hashtable.put(1, "老张");
        hashtable.put(2, "老王");
        hashtable.put(3, "老李");
        hashtable.put(4, "老赵");
        hashtable.put(5, "老孙");
//        hashtable.put(null, "老冯");  // java.lang.NullPointerException
//        hashtable.put(6, null); // java.lang.NullPointerException

        Set<Map.Entry<Integer, String>> entries = hashtable.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        /*
        * 5:老孙
        * 4:老赵
        * 3:老李
        * 2:老王
        * 1:老张
        * */
    }
}

3.特有方法

ublic class HashtableTest {
    public static void main(String[] args) {
        Hashtable<Integer, String> hashtable = new Hashtable<>();
        hashtable.put(1, "老张");
        hashtable.put(2, "老王");
        hashtable.put(3, "老李");

        /**
         * 特有方法
         */
        // 获取所有的key
        Enumeration<Integer> keys = hashtable.keys();
        while (keys.hasMoreElements()) {
            System.out.print(keys.nextElement() + "\t");
        }
        // 3	2	1

        System.out.println();
        // 获取所有的value
        Enumeration<String> values = hashtable.elements();
        while (values.hasMoreElements()) {
            System.out.print(values.nextElement() + "\t");
        }
        // 老李	老王	老张
    }
}

四、Properties

1.说明

  1. 属性类;
  2. 继承 Hashtable,也是一个线程安全的 Map 集合;
  3. 一般和 java 程序中属性配置文件联合使用;
  4. 该类不支持泛型,key 和 value 都是固定的 String 类型。

2.实例

public class PropertiesTest {
    public static void main(String[] args) {
        Properties p = new Properties();
        p.setProperty("name", "张三");
        p.setProperty("age", "18");
        p.setProperty("sex", "男");
        p.setProperty("address", "北京");

        System.out.println(p.getProperty("name"));  // 张三
        System.out.println(p.getProperty("age"));   // 18
        System.out.println(p.getProperty("sex"));   // 男
        System.out.println(p.getProperty("address"));   // 北京

        Enumeration<?> enumeration = p.propertyNames();
        while (enumeration.hasMoreElements()) {
            String name = (String) enumeration.nextElement();
            String value = p.getProperty(name);
            System.out.println(name + ":" + value);
        }
        /*
        * address:北京
        * age:18
        * name:张三
        * sex:男
        * */
    }
}

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

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

相关文章

浅谈Linux中的Shell及其原理

浅谈Linux中的Shell及其原理 Linux中Shell的运行原理github地址前言一、Linux内核与Shell的关系1.1 操作系统核心1.2 用户与内核的隔离 二、Shell的演进与核心机制2.1 发展历程2.2 核心功能解析2.3 shell的工作流程1. 用户输入命令2. 解析器拆分指令3. 扩展器处理动态内容变量替…

ARM内部寄存器与常用汇编指令(ARM汇编)

1 地址空间&RISC与CISC CISC&#xff1a;复杂指令集计算机 RISC&#xff1a;精简指令集计算机 RISC 与 CISC 的比较 2 ARM内部寄存器 3 ARM汇编概述 RISC精简指令可以大致分为几类&#xff1a; 1.内存读写 2.运算 3.跳转/分支 4.比较 而指令的集合就称之为“指令集”…

【QT5 Widgets示例】Model/View编程初探

文章目录 Model/ViewModel/View编程的优点常见Model类和View类Model/View应用程序示例只读的表格修改文本外观显示变化的数据设置表格标头可编辑视图示例树结构视图示例获取视图选中项 Model/View Model/View编程的优点 Model/View编程介绍&#xff1a;https://doc.qt.io/qt-…

一键优化右键菜单,高效又清爽!

打工人们你们好&#xff01;这里是摸鱼 特供版~ 电脑右键菜单杂乱无章&#xff0c;常用功能被淹没&#xff1f;图标显示异常、打印出错让人手足无措&#xff1f;别担心&#xff0c;Easy Context Menu来帮你&#xff01;这是一款右键菜单管理工具&#xff0c;能快速清理不必要的…

成绩排序(结构体排序)

成绩排序 #include<stdio.h> #include<stdlib.h> struct student{char name[50];int score;int order; }; int flag; int cmp(const void *a,const void *b){struct student *s1(struct student*)a;struct student *s2(struct student*)b;if(s1->scores2->…

OpenHarmony-XTS测试

OpenHarmony-XTS测试 OpenHarmony-XTS测试环境搭建测试准备开始运行PS OpenHarmony-XTS测试 针对OpenHarmony版本进行XTS测试使用记录。 windows环境。 以acts套件为例。 环境搭建 获取测试套件&#xff0c;两种方法 1&#xff09;官网下载&#xff1a;https://www.openharm…

【物联网-WIFI】

物联网-WIFI ■ ESP32-C3-模块简介■ ESP32-C3-■ ESP32-C3-■ WIFI-模组■ WIFI-■ WIFI- ■ ESP32-C3-模块简介 ■ ESP32-C3- ■ ESP32-C3- ■ WIFI-模组 ■ WIFI- ■ WIFI-

linux常用基本指令汇总

文章目录 01. ls指令02. pwd指令03. cd指令04. touch指令05. mkdir指令06. rmdir指令07. rm指令08. man指令09. cp指令10. mv指令11. cat指令11. more指令12. less指令13. head指令14. tail指令15. time指令16. cal指令17. find指令18. grep指令19. zip/unzip指令20.tar指令21.…

Docker Desktop 安装与使用详解

目录 1. 前言2. Docker Desktop 安装2.1 下载及安装2.2 登录 Docker 账号2.3 进入 Docker Desktop 主界面 3. Docker 版本查看与环境检查3.1 查看 Docker Desktop 支持的 Docker 和 Kubernetes 版本3.2 检查 Docker 版本 4. Docker Hub 和常用镜像管理方式4.1 使用 Docker Hub4…

【HarmonyOS Next】鸿蒙应用常规面试题和答辩思路参考

【HarmonyOS Next】鸿蒙应用常规面试题和答辩思路参考 一、充分了解岗位JD要求 根据招聘发布的岗位JD&#xff0c;进行自我匹配分析。了解基本要求和加分项&#xff0c;以及项目节奏和英文要求等。 技术不匹配的点&#xff0c;是否会影响应聘岗位加分项自己是否掌握&#xf…

《计算机图形学》第二课笔记-----二维变换的推导

前言&#xff1a;为什么这么突兀的把这一节内容放在了第二课&#xff0c;第一是因为我急于求成&#xff0c;第二是因为这一章节太重要了&#xff0c;这几乎是二维三维变换的最核心的东西&#xff0c;理解了这一章节内容&#xff0c;后面的就会像打通了任督二脉一样&#xff0c;…

机器学习(七)

一&#xff0c;监督学习和无监督学习聚类的数据集比较&#xff1a; 监督学习&#xff1a; 数据集包括输入的数据和与之对应的标签 无监督学习&#xff1a; 数据集仅含有输入的数据&#xff0c;要求算法自己通过所给的数据集来确定决策边界 二&#xff0c;聚类(Clustering): 聚…

利用labelimg实现yolov8数据集的制作

我们在使用yolov8进行物体检测识别的时候&#xff0c;由于其内置的n,s,m等模型只包含90多种物体&#xff08;很多其他物品并未包含在其中&#xff09;&#xff0c;导致我们无法直接使用其模型进行视频或者图片的检测识别。这个时候&#xff0c;我们就需要自己制作数据集进行训练…

【0x80070666】-已安装另一个版本...(Tableau 安装失败)

第一种是之前安装过tableau相关软件&#xff0c;但是没卸载干净。 方法1&#xff1a;卸载旧版本 打开 控制面板 → 程序和功能&#xff08;或 添加/删除程序&#xff09;。查找 Tableau Desktop&#xff0c;如果已安装旧版本&#xff0c;卸载它。重新启动电脑后再尝试安装。 …

Word填写窗口功能详解:如何让文档填写更高效?

在日常办公中&#xff0c;我们经常需要让他人填写一些固定格式的文档&#xff0c;比如合同、申请表、调查问卷等。如果直接使用普通文本编辑&#xff0c;填写时可能会破坏排版&#xff0c;甚至修改了不该改动的内容。这时候&#xff0c;Word的填写窗口&#xff08;即“内容控件…

Oracle数据库存储结构--逻辑存储结构

数据库存储结构&#xff1a;分为物理存储结构和逻辑存储结构。 物理存储结构&#xff1a;操作系统层面如何组织和管理数据 逻辑存储结构&#xff1a;Oracle数据库内部数据组织和管理数据&#xff0c;数据库管理系统层面如何组织和管理数据 Oracle逻辑存储结构 数据库的逻…

简单创建一个Django项目并配置neo4j数据库

创建项目&#xff0c;项目的文件夹就是项目的名称 创建项目的基本框架 安装djangorestframework 单击运行 查看浏览器运行效果&#xff1a; 运行效果如下&#xff1a; 创建应用(假如说是创建一个名为myapp的应用)&#xff1a; python manage.py startapp myapp创建之后的…

java实现智能家居控制系统——入门版

文章目录 一、需求二、业务分析三、具体实现创建一个功能接口&#xff0c;实现设备的开关创建一个家电类&#xff0c;作为功能接口的实现类&#xff0c;定义名字和状态分别创建电视机、洗衣机、电灯的类&#xff0c;继承家电类Tv类WashMachine类Lamp类 定义智能控制系统类&…

VSCode C/C++ 开发环境完整配置及常见问题(自用)

这里主要记录了一些与配置相关的内容。由于网上教程众多&#xff0c;部分解决方法并不能完全契合我遇到的问题&#xff0c;因此我选择以自己偏好的方式&#xff0c;对 VSCode 进行完整的配置&#xff0c;并记录在使用过程中遇到的问题及解决方案。后续内容也会持续更新和完善。…

实用小工具——快速获取数据库时间写法

最近我遇到了一个比较棘手的问题&#xff1a;在工作中&#xff0c;各个项目所使用的数据库类型各不相同。这导致我习惯性地使用Oracle的SQL语句进行编写&#xff0c;但每次完成后都会遇到报错&#xff0c;最终才意识到项目的数据库并非Oracle。为了避免这种情况&#xff0c;我需…