Java 集合:Map 接口完整笔记

一、Map 集合体系

Map 是 Map 集合体系的根接口

a) Map 集合特点

  1. 键值对存储key‑value,一个键值对就是 Map 集合当中的一个元素
  2. key(键):无序、无下标,元素不允许重复,具有唯一性
  3. value(值):无序、无下标,元素允许重复

b) 常用方法

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

⭐开发重点方法

  1. V remove(K key) 根据键删除对应的键值对,返回被删除的 value。
  2. V get(K key) 根据 key 获取对应的 value。⭐重点
  3. int size() 获取 Map 当中键值对的总个数。
  4. boolean containsKey(K key) 判断集合是否包含指定 key,存在返回 true,不存在返回 false。
  5. 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()。 开发中一般优先使用StringInteger作为 key(用户名、id 等),检索效率更高。

  1. HashMap JDK1.2,线程不安全,运行效率高;允许null作为键、允许null作为值。

底层结构:JDK1.8 之前 = 数组 + 链表;JDK1.8 及之后 = 数组 + 链表 + 红黑树(链表长度超过 8 且数组长度≥64 时转红黑树)

  1. Hashtable JDK1.0,线程安全,运行效率慢;不允许null作为键和值。

📝面试题:写出 HashMap 和 Hashtable 的区别

表格

对比维度HashMapHashtable
出现版本JDK1.2JDK1.0
线程安全不安全,效率高安全,方法加 synchronized,效率低
null 键 /null 值都允许都不允许,抛 NullPointerException
父类AbstractMapDictionary
迭代器Iterator(fail-fast 快速失败)Enumerator
默认初始容量1611
扩容机制2 倍2 倍 + 1
  1. LinkedHashMap HashMap 的子类,可以保留元素的添加顺序;底层 = HashMap + 双向链表。
  2. 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);
    }
}
  1. 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(推荐)、HashtableCollections.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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值