Java集合——Map集合的继承结构、遍历方法、HashMap的底层结构,put方法的执行过程

一、Map的继承结构

在这里插入图片描述

  • Map集合以key和value的键值对形式存储。key和value存储的都是引用
  • Map集合中key起主导作用。value是附属在key上的。
  • SequencedMap是Java21新增的。LinkedHashMap和TreeMap都是有序集合。(key是有序的)
  • HashMap,Hashtable,Properties都是无序集合。(key是无序的)
  • Map集合的key都是不可重复的。key重复的话,value会覆盖。
  • HashSet集合底层是new了一个HashMap。往HashSet集合中存储元素实际上是将元素存储到HashMap集合的key部分。HashMap集合的key是无序不可重复的,因此HashSet集合就是无序不可重复的。HashMap集合底层是哈希表/散列表数据结构,因此HashSet底层也是哈希表/散列表。
  • TreeSet集合底层是new了一个TreeMap。往TreeSet集合中存储元素实际上是将元素存储到TreeMap集合的key部分。TreeMap集合的key是不可重复但可排序的,因此TreeSet集合就是不可重复但可排序的。TreeMap集合底层是红黑树,因此TreeSet底层也是红黑树。它们的排序通过java.lang.Comparablejava.util.Comparator均可实现。
  • LinkedHashSet集合底层是new了一个LinkedHashMap。LinkedHashMap集合只是为了保证元素的插入顺序,效率比HashSet低,底层采用的哈希表+双向链表实现。
  • 根据源码可以看到向Set集合中add时,底层会向Map中put。value只是一个固定不变的常量,只是起到一个占位符的作用。主要是key。
  • Hashtable和Properties的key和value都不能为null,TreeMap的key不能为null,TreeSet不能添加null。

二、Map接口的常用方法

在这里插入图片描述
第13个方法是Java 9中引入的一种方便的方式来创建Map实例。

import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> scores = Map.of("Alice", 80, "Bob", 90, "Charlie", 75);
        System.out.println(scores);
    }
}

输出结果为:

{Alice=80, Bob=90, Charlie=75}

三、Map集合的遍历

1、keySet

import java.util.Set;

public class MapIterationTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();

        map.put(1, "zhangsan");
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");
	}
}

使用迭代器

Set<Integer> keySet = map.keySet();
Iterator<Integer> iterator = keySet.iterator();
while (iterator.hasNext()) {
    Integer next = iterator.next();
    System.out.println(next + " = " +map.get(next ));
}

使用for-each

Set<Integer> keySet = map.keySet();
for (Integer key : keySet){
   System.out.println(key + "="+map.get(key));
}

2、entrySet(效率较高)

在这里插入图片描述

Entry类中包含获取key和value的方法,不需要根据key去找value

Set<Map.Entry<Integer, String>> entries = map.entrySet();
     Iterator<Map.Entry<Integer, String>> entryIterator = entries.iterator();
     while (entryIterator.hasNext()) {
         Map.Entry<Integer, String> entry = entryIterator.next();
         Integer key = entry.getKey();
         String value = entry.getValue();
         System.out.println(key + "=" +value);

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

三、HashMap底层结构

1、重写equals和hashCode方法

map的底层数据结构是由哈希表和红黑树组成的,当自定义map的key为引用时,需要重写这个引用的toString和hashCode方法。
在这里插入图片描述

一个重要原理:如果equals方法返回结果为true,那么hashCode()方法返回的结果必须相同。这样才能保证key不是重复。存储HashMap集合key部分的元素,以及存储在HashSet集合中的元素,都需要同时重写hashCode+equals

public class User {
    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
        @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

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

新建两个相同的对象user1和user2,将user1和user2作为key放入map集合中,User类在不重写equals和hashCode方法的前提下,会将user1和user2都放入,但是这违背了HashMap集合key的唯一性。重写equals方法判断对象是否相同,重写hashCode方法保证相同的对象经过hash函数处理之后值是相同的,发生哈希碰撞,从而保证key的唯一性。

package com.rongrong.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        User user = new User("rongrong",18);
        User user1 = new User("rongrong",18);

        Map<User,Integer> map = new HashMap();
        map.put(user,123);
        map.put(user1,456);

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

这两个方法不重写,会调用Object类的equals和hashCode的方法,而Object类中的这两个方法根据引用的地址判断两个对象是否相同和求哈希值。

2、HashMap在Java8后的改进

在这里插入图片描述

3、HashMap初始化容量

在这里插入图片描述
在这里插入图片描述

4、HashMap的put方法执行过程

HashMap集合扩容成本较高,扩容之后,数组长度会改变,需要将集合中的元素,重新计算分配,让其散列分布均匀。HashMap中的put方法执行过程大体如下:

1、判断键值对数组table[i]是否为空(null)或者length=0,或者是的话就执行resize()方法进行扩容。
在这里插入图片描述

2、不是就根据键值key计算hash值得到插入的数组索引i。
3、判断table[i]==null,如果是true,直接新建节点进行添加。
在这里插入图片描述
4、如果table[i]!=null,判断table[i]处的key是否和要插入的key相同,相同则覆盖,不同则执行5
在这里插入图片描述
5、判断table[i]是否为treenode,即判断是否是红黑树,如果是红黑树,直接在树中插入键值对。
在这里插入图片描述
6、如果不是treenode,开始遍历链表,

  • 判断链表长度是否大于8,如果大于8并且此时table的长度大于64,就转成红黑树,在树中执行插入操作;
  • 如果链表长度大于8但是table长度小于64,执行resize()扩容方法。
  • 如果不大于8,就在链表中执行插入;在遍历过程中判断key是否存在,存在就直接覆盖对应的value值。
    在这里插入图片描述

7、插入成功后,就需要判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过了,执行resize方法进行扩容。
在这里插入图片描述

四、LinkedHashMap

在这里插入图片描述

在这里插入图片描述

五、Properties初步了解

在这里插入图片描述

  • 50
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值