云计算百科
云计算领域专业知识百科平台

Java 集合:Map 接口完整笔记

一、Map 集合体系

Map 是 Map 集合体系的根接口

a) Map 集合特点

  • 键值对存储key‑value,一个键值对就是 Map 集合当中的一个元素
  • key(键):无序、无下标,元素不允许重复,具有唯一性
  • value(值):无序、无下标,元素允许重复
  • b) 常用方法

  • V put(K key,V value) 往 Map 集合添加键值对。
    • 如果集合中不存在该 key:直接新增,返回null
    • 如果集合已经存在该 key:新 value 覆盖旧 value,返回被覆盖的旧 value

    ⭐开发重点方法

  • V remove(K key) 根据键删除对应的键值对,返回被删除的 value。
  • V get(K key) 根据 key 获取对应的 value。⭐重点
  • int size() 获取 Map 当中键值对的总个数。
  • boolean containsKey(K key) 判断集合是否包含指定 key,存在返回 true,不存在返回 false。
  • boolean containsValue(V value) 判断集合是否包含指定 value,存在返回 true,不存在返回 false。
  • 常用方法完整代码示例

    import java.util.HashMap;
    import java.util.Map;

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

    // 1.put 添加键值对
    map.put("张三", 18);
    map.put("李四", 20);
    Integer oldValue = map.put("张三", 25); // key已存在,覆盖旧值,返回18
    System.out.println("被覆盖的旧值:" + oldValue);

    // 2.size 获取键值对个数
    System.out.println("集合大小:" + map.size());

    // 3.get 根据key获取value
    System.out.println("张三的年龄:" + map.get("张三"));

    // 4.containsKey 判断是否包含key
    System.out.println("是否包含王五:" + map.containsKey("王五"));

    // 5.containsValue 判断是否包含value
    System.out.println("是否包含年龄25:" + map.containsValue(25));

    // 6.remove 根据key删除
    Integer removed = map.remove("李四");
    System.out.println("被删除的值:" + removed);
    }
    }

    c) Map 实现类

    如果自定义类对象作为 HashMap 的键,为保证 key 不重复,对应类必须重写hashCode()和equals()。 开发中一般优先使用String、Integer作为 key(用户名、id 等),检索效率更高。

  • HashMap JDK1.2,线程不安全,运行效率高;允许null作为键、允许null作为值。
  • 底层结构:JDK1.8 之前 = 数组 + 链表;JDK1.8 及之后 = 数组 + 链表 + 红黑树(链表长度超过 8 且数组长度≥64 时转红黑树)

  • Hashtable JDK1.0,线程安全,运行效率慢;不允许null作为键和值。
  • 📝面试题:写出 HashMap 和 Hashtable 的区别

    表格

    对比维度HashMapHashtable
    出现版本 JDK1.2 JDK1.0
    线程安全 不安全,效率高 安全,方法加 synchronized,效率低
    null 键 /null 值 都允许 都不允许,抛 NullPointerException
    父类 AbstractMap Dictionary
    迭代器 Iterator(fail-fast 快速失败) Enumerator
    默认初始容量 16 11
    扩容机制 2 倍 2 倍 + 1
  • LinkedHashMap HashMap 的子类,可以保留元素的添加顺序;底层 = HashMap + 双向链表。
  • Properties Hashtable 的子类,key 与 value 都必须是字符串类型;常用于读取加载配置文件.properties。
  • import java.util.Properties;
    import java.io.FileInputStream;

    public class PropertiesDemo {
    public static void main(String[] args) throws Exception {
    Properties prop = new Properties();
    // 加载配置文件
    prop.load(new FileInputStream("config.properties"));
    // 根据key获取value
    String username = prop.getProperty("username");
    System.out.println(username);
    }
    }

  • TreeMap SortedMap 接口实现类,可以对集合中的 key 进行排序;key 必须实现 Comparable 接口或传入 Comparator 比较器。
  • d) Map 三种遍历方式

    Map 本身没有索引,不能直接 for 循环遍历,提供三种遍历方案

    ① 键遍历 keySet ()

    获取全部 key 组成 Set 集合,再通过 key 调用 get () 拿到 value

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

    ② 值遍历 values ()

    只获取集合中所有 value,拿不到 key

    Collection<V> vs = map.values();
    for(V value : vs){
    System.out.println(value);
    }

    注意:Map 没有通过 value 反向获取 key 的方法

    ③ 键值对遍历 entrySet ()(效率较高,推荐)

    把每一组key‑value封装为Map.Entry对象

    Set<Map.Entry<K,V>> kvs = map.entrySet();
    for(Map.Entry<K,V> kv : kvs){
    K key = kv.getKey();
    V value = kv.getValue();
    System.out.println(key + " = " + value);
    }

    三种遍历完整可运行代码

    import java.util.*;

    public class MapIterator {
    public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    map.put("Java", 100);
    map.put("Python", 95);
    map.put("C++", 90);

    // 方式1:keySet 键遍历
    System.out.println("===== keySet遍历 =====");
    Set<String> keys = map.keySet();
    for (String key : keys) {
    System.out.println(key + " -> " + map.get(key));
    }

    // 方式2:values 值遍历
    System.out.println("===== values遍历 =====");
    Collection<Integer> values = map.values();
    for (Integer value : values) {
    System.out.println(value);
    }

    // 方式3:entrySet 键值对遍历(推荐)
    System.out.println("===== entrySet遍历 =====");
    Set<Map.Entry<String, Integer>> entries = map.entrySet();
    for (Map.Entry<String, Integer> entry : entries) {
    System.out.println(entry.getKey() + " -> " + entry.getValue());
    }
    }
    }


    二、HashMap 高频面试题补充

    1. HashMap 底层原理

    • JDK1.7:数组 + 链表,头插法
    • JDK1.8:数组 + 链表 + 红黑树,尾插法
    • 链表转红黑树条件:链表长度 ≥ 8 且 数组长度 ≥ 64
    • 红黑树退化为链表条件:节点数 ≤ 6

    2. HashMap 扩容机制

    • 默认初始容量:16
    • 负载因子:0.75
    • 扩容阈值 = 容量 × 负载因子 = 16 × 0.75 = 12
    • 元素个数达到阈值时,扩容为原来的2 倍
    • 扩容时会重新计算所有元素的 hash 位置(rehash)

    3. HashMap 为什么线程不安全?

    • 多线程同时 put 可能导致数据覆盖
    • JDK1.7 扩容时头插法可能导致链表成环,死循环
    • 解决方案:ConcurrentHashMap(推荐)、Hashtable、Collections.synchronizedMap()

    4. 自定义对象作为 key 注意事项

    必须同时重写hashCode()和equals():

    • hashCode 相同:对象不一定相同,需要再用 equals 判断
    • equals 相同:hashCode 一定相同
    • 只重写 equals 不重写 hashCode:两个逻辑相等的对象会被存到不同位置,无法去重

    class Student {
    private String name;
    private int 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);
    }
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Java 集合:Map 接口完整笔记
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!