当前位置: 首页 > news >正文

Java基础关键_021_集合(五)

目  录

一、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:男
        * */
    }
}

相关文章:

  • Spring TX配置(声明式事务管理+annotation)
  • 计算矩阵边缘元素之和(信息学奥赛一本通-1121)
  • Python 实现的采集诸葛灵签
  • 研发团队协作软件推荐:18款工具对比
  • win10升级到22H2版本后无法联网
  • 01-Canvas-使用fabric初始
  • 从零搭建微服务项目Pro(第2-2章——JSR303自定义文件校验+整合至微服务公共模块)
  • Java使用JDBC连接操作Sqlite 笔记250314
  • 【算法】 【c++】字符串s1 中删除所有 s2 中出现的字符
  • 总结 HTTP 协议的基本格式, 相关知识以及抓包工具fiddler的使用
  • 67.Harmonyos NEXT 图片预览组件之性能优化策略
  • 【Scala】
  • 论文分享 | HE-Nav: 一种适用于复杂环境中空地机器人的高性能高效导航系统
  • TIA博途在编译 PLC时出现崩溃的解决方法
  • 测不准关系
  • 【redis】zset 类型:基本命令(上)
  • Java中架构DDD:理解聚合、实体和值对象三种核心构造块
  • C#+EF+SqlServer性能优化笔记
  • Python 科学计算与机器学习入门:NumPy + Scikit-Learn 实战指南
  • 大数据-spark3.5安装部署之local模式
  • 国际金价下跌,中概股多数上涨,穆迪下调美国主权信用评级
  • 澎湃与七猫联合启动百万奖金征文,赋能非虚构与现实题材创作
  • 定制基因编辑疗法治愈罕见遗传病患儿
  • 马上评|清理“滥竽充数者”,为医者正名
  • 湖北宜化拟斥资超32亿加价回购“弃子”,布局上游煤炭业务
  • 江西3人拟提名为县(市、区)长候选人