1. Map接口概述与核心方法

Map(映射)是Java集合框架中用于存储键值对(Key-Value)的数据结构接口。与Collection接口不同,Map中的每个元素都包含一个键和一个值,键是唯一的,值可以重复。

1.1 Map接口的核心方法

import java.util.*;

public class MapBasicDemo {
    public static void main(String[] args) {
        // 创建Map实例(使用HashMap实现)
        Map<String, Integer> studentScores = new HashMap<>();
        
        // 1. put() - 添加键值对
        studentScores.put("张三", 85);
        studentScores.put("李四", 92);
        studentScores.put("王五", 78);
        System.out.println("添加后: " + studentScores);
        
        // 2. get() - 根据键获取值
        Integer liScore = studentScores.get("李四");
        System.out.println("李四的成绩: " + liScore);
        
        // 3. containsKey() - 检查键是否存在
        boolean hasWang = studentScores.containsKey("王五");
        System.out.println("是否包含王五: " + hasWang);
        
        // 4. containsValue() - 检查值是否存在
        boolean hasScore92 = studentScores.containsValue(92);
        System.out.println("是否有92分: " + hasScore92);
        
        // 5. remove() - 删除键值对
        Integer removedScore = studentScores.remove("张三");
        System.out.println("删除张三后: " + studentScores);
        
        // 6. size() - 获取元素数量
        System.out.println("当前元素数量: " + studentScores.size());
        
        // 7. keySet() - 获取所有键的集合
        Set<String> keys = studentScores.keySet();
        System.out.println("所有键: " + keys);
        
        // 8. values() - 获取所有值的集合
        Collection<Integer> values = studentScores.values();
        System.out.println("所有值: " + values);
        
        // 9. entrySet() - 获取所有键值对的集合
        Set<Map.Entry<String, Integer>> entries = studentScores.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println("键: " + entry.getKey() + ", 值: " + entry.getValue());
        }
        
        // 10. clear() - 清空Map
        studentScores.clear();
        System.out.println("清空后大小: " + studentScores.size());
    }
}

1.2 Map的三种遍历方式

public class MapTraversalDemo {
    public static void main(String[] args) {
        Map<String, String> countryCapital = new HashMap<>();
        countryCapital.put("中国", "北京");
        countryCapital.put("美国", "华盛顿");
        countryCapital.put("日本", "东京");
        countryCapital.put("英国", "伦敦");
        
        System.out.println("=== 方式1: 使用keySet()遍历 ===");
        for (String country : countryCapital.keySet()) {
            String capital = countryCapital.get(country);
            System.out.println(country + " -> " + capital);
        }
        
        System.out.println("\n=== 方式2: 使用entrySet()遍历(推荐)===");
        for (Map.Entry<String, String> entry : countryCapital.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
        
        System.out.println("\n=== 方式3: 使用forEach + Lambda表达式(Java 8+)===");
        countryCapital.forEach((key, value) -> 
            System.out.println(key + " -> " + value)
        );
        
        System.out.println("\n=== 方式4: 使用迭代器 ===");
        Iterator<Map.Entry<String, String>> iterator = countryCapital.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }
}

2. HashMap实战详解

HashMap是基于哈希表实现的Map接口,它提供了常数时间复杂度的基本操作(get和put),但不保证元素的顺序。

2.1 HashMap的基本使用

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

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建HashMap
        HashMap<String, Employee> employeeMap = new HashMap<>();
        
        // 添加元素
        employeeMap.put("E001", new Employee("张三", "开发部", 15000));
        employeeMap.put("E002", new Employee("李四", "测试部", 12000));
        employeeMap.put("E003", new Employee("王五", "产品部", 13000));
        
        // 获取元素
        Employee emp = employeeMap.get("E002");
        System.out.println("E002员工信息: " + emp);
        
        // 检查键是否存在
        if (employeeMap.containsKey("E001")) {
            System.out.println("E001存在");
        }
        
        // 更新元素
        employeeMap.put("E001", new Employee("张三", "开发部", 16000));
        System.out.println("更新后E001: " + employeeMap.get("E001"));
        
        // 删除元素
        employeeMap.remove("E003");
        System.out.println("删除E003后大小: " + employeeMap.size());
        
        // 遍历HashMap
        System.out.println("\n=== 所有员工信息 ===");
        for (Map.Entry<String, Employee> entry : employeeMap.entrySet()) {
            System.out.println("工号: " + entry.getKey() + ", 信息: " + entry.getValue());
        }
    }
}

class Employee {
    private String name;
    private String department;
    private double salary;
    
    public Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }
    
    @Override
    public String toString() {
        return String.format("Employee{name='%s', department='%s', salary=%.2f}", 
                           name, department, salary);
    }
}

2.2 HashMap的哈希冲突与扩容机制

public class HashMapInternalDemo {
    public static void main(String[] args) {
        // 演示HashMap的初始容量和负载因子
        HashMap<String, Integer> map = new HashMap<>(16, 0.75f);
        
        // 添加16个元素,观察扩容
        for (int i = 0; i < 16; i++) {
            map.put("key" + i, i);
            System.out.printf("添加第%d个元素,当前大小: %d%n", i + 1, map.size());
        }
        
        // 第13个元素会触发扩容(16 * 0.75 = 12)
        System.out.println("\nHashMap内部结构说明:");
        System.out.println("1. 初始容量: 16");
        System.out.println("2. 负载因子: 0.75");
        System.out.println("3. 扩容阈值: 容量 * 负载因子 = 12");
        System.out.println("4. 当元素数量超过12时,HashMap会自动扩容为原来的2倍(32)");
        
        // 演示哈希冲突
        System.out.println("\n=== 哈希冲突演示 ===");
        HashMap<BadHashKey, String> conflictMap = new HashMap<>();
        
        // 创建哈希值相同的键
        BadHashKey key1 = new BadHashKey(1);
        BadHashKey key2 = new BadHashKey(17); // 16 + 1,哈希冲突
        BadHashKey key3 = new BadHashKey(33); // 32 + 1,哈希冲突
        
        conflictMap.put(key1, "值1");
        conflictMap.put(key2, "值2");
        conflictMap.put(key3, "值3");
        
        System.out.println("冲突Map内容: " + conflictMap);
        System.out.println("key1哈希值: " + key1.hashCode());
        System.out.println("key2哈希值: " + key2.hashCode());
        System.out.println("key3哈希值: " + key3.hashCode());
    }
}

// 故意制造哈希冲突的键类
class BadHashKey {
    private int id;
    
    public BadHashKey(int id) {
        this.id = id;
    }
    
    @Override
    public int hashCode() {
        // 所有实例返回相同的哈希值,制造严重冲突
        return 1;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        BadHashKey that = (BadHashKey) obj;
        return id == that.id;
    }
    
    @Override
    public String toString() {
        return "BadHashKey{id=" + id + "}";
    }
}

2.3 HashMap的性能优化实践

import java.util.HashMap;

public class HashMapOptimization {
    
    // 优化1: 合理设置初始容量,避免频繁扩容
    public static void optimizedHashMap() {
        // 已知要存储1000个元素,设置初始容量为1024(2的幂)
        // 1024 * 0.75 = 768,足够存储1000个元素而不会触发扩容
        HashMap<String, String> optimizedMap = new HashMap<>(1024);
        
        for (int i = 0; i < 1000; i++) {
            optimizedMap.put("key" + i, "value" + i);
        }
        System.out.println("优化后的Map大小: " + optimizedMap.size());
    }
    
    // 优化2: 使用合适的键对象,实现良好的hashCode()和equals()
    static class OptimizedKey {
        private final String id;
        private final String type;
        
        public OptimizedKey(String id, String type) {
            this.id = id;
            this.type = type;
        }
        
        @Override
        public int hashCode() {
            // 使用Objects.hash()生成复合哈希码
            return 31 * id.hashCode() + type.hashCode();
        }
        
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            OptimizedKey that = (OptimizedKey) obj;
            return id.equals(that.id) && type.equals(that.type);
        }
    }
    
    // 优化3: 使用computeIfAbsent()进行条件性计算
    public static void computeIfAbsentDemo() {
        HashMap<String, Integer> wordCount = new HashMap<>();
        String text = "hello world hello java world java";
        String[] words = text.split(" ");
        
        for (String word : words) {
            // 如果键不存在,使用提供的函数计算值
            wordCount.computeIfAbsent(word, k -> 0);
            // 递增计数
            wordCount.computeIfPresent(word, (k, v) -> v + 1);
        }
        
        System.out.println("单词计数: " + wordCount);
    }
    
    // 优化4: 使用merge()进行合并操作
    public static void mergeDemo() {
        HashMap<String, Integer> map1 = new HashMap<>();
        map1.put("A", 1);
        map1.put("B", 2);
        
        HashMap<String, Integer> map2 = new HashMap<>();
        map2.put("B", 3);
        map2.put("C", 4);
        
        // 合并两个Map
        map2.forEach((key, value) -> 
            map1.merge(key, value, Integer::sum)
        );
        
        System.out.println("合并后的Map: " + map1);
    }
    
    public static void main(String[] args) {
        System.out.println("=== HashMap性能优化实践 ===");
        optimizedHashMap();
        computeIfAbsentDemo();
        mergeDemo();
    }
}

3. TreeMap与SortedMap接口

TreeMap是基于红黑树(Red-Black Tree)实现的NavigableMap,它保证了元素按照键的自然顺序或自定义比较器顺序排列。

3.1 TreeMap的基本使用

import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        System.out.println("=== 1. 自然顺序的TreeMap ===");
        TreeMap<Integer, String> naturalOrderMap = new TreeMap<>();
        naturalOrderMap.put(3, "Three");
        naturalOrderMap.put(1, "One");
        naturalOrderMap.put(4, "Four");
        naturalOrderMap.put(2, "Two");
        
        System.out.println("自然顺序: " + naturalOrderMap);
        System.out.println("第一个键: " + naturalOrderMap.firstKey());
        System.out.println("最后一个键: " + naturalOrderMap.lastKey());
        
        System.out.println("\n=== 2. 自定义比较器的TreeMap ===");
        // 按字符串长度排序
        TreeMap<String, Integer> lengthOrderMap = new TreeMap<>(
            Comparator.comparingInt(String::length)
                .thenComparing(Comparator.naturalOrder())
        );
        
        lengthOrderMap.put("apple", 1);
        lengthOrderMap.put("banana", 2);
        lengthOrderMap.put("cat", 3);
        lengthOrderMap.put("dog", 4);
        lengthOrderMap.put("elephant", 5);
        
        System.out.println("按长度排序: " + lengthOrderMap);
        
        System.out.println("\n=== 3. TreeMap的范围查询 ===");
        TreeMap<Integer, String> scoreMap = new TreeMap<>();
        for (int i = 1; i <= 10; i++) {
            scoreMap.put(i * 10, "Student" + i);
        }
        
        System.out.println("原始Map: " + scoreMap);
        System.out.println("大于等于50: " + scoreMap.tailMap(50));
        System.out.println("小于70: " + scoreMap.headMap(70));
        System.out.println("30到80之间: " + scoreMap.subMap(30, 80));
        
        System.out.println("\n=== 4. 导航方法演示 ===");
        System.out.println("小于40的最大键: " + scoreMap.lowerKey(40));
        System.out.println("小于等于40的最大键: " + scoreMap.floorKey(40));
        System.out.println("大于40的最小键: " + scoreMap.higherKey(40));
        System.out.println("大于等于40的最小键: " + scoreMap.ceilingKey(40));
        
        System.out.println("\n=== 5. 逆序视图 ===");
        NavigableMap<Integer, String> descendingMap = scoreMap.descendingMap();
        System.out.println("逆序Map: " + descendingMap);
        
        System.out.println("\n=== 6. 学生成绩排序示例 ===");
        TreeMap<Student, Integer> studentScores = new TreeMap<>(
            Comparator.comparing(Student::getScore).reversed()
                .thenComparing(Student::getName)
        );
        
        studentScores.put(new Student("张三", 85), 1);
        studentScores.put(new Student("李四", 92), 2);
        studentScores.put(new Student("王五", 85), 3); // 同分按姓名排序
        studentScores.put(new Student("赵六", 78), 4);
        
        System.out.println("按成绩降序排列:");
        studentScores.forEach((student, rank) -> 
            System.out.println("排名" + rank + ": " + student.getName() + " - " + student.getScore())
        );
    }
}

class Student {
    private String name;
    private int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    
    public String getName() { return name; }
    public int getScore() { return score; }
}

3.2 TreeMap与HashMap的性能对比

import java.util.*;

public class MapPerformanceComparison {
    
    public static void performanceTest() {
        final int ELEMENT_COUNT = 100000;
        Random random = new Random();
        
        // 准备测试数据
        List<Integer> keys = new ArrayList<>();
        List<String> values = new ArrayList<>();
        
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            keys.add(random.nextInt(1000000));
            values.add("Value" + i);
        }
        
        // HashMap性能测试
        long hashMapStart = System.currentTimeMillis();
        HashMap<Integer, String> hashMap = new HashMap<>();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            hashMap.put(keys.get(i), values.get(i));
        }
        long hashMapPutTime = System.currentTimeMillis() - hashMapStart;
        
        long hashMapGetStart = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            hashMap.get(keys.get(random.nextInt(ELEMENT_COUNT)));
        }
        long hashMapGetTime = System.currentTimeMillis() - hashMapGetStart;
        
        // TreeMap性能测试
        long treeMapStart = System.currentTimeMillis();
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            treeMap.put(keys.get(i), values.get(i));
        }
        long treeMapPutTime = System.currentTimeMillis() - treeMapStart;
        
        long treeMapGetStart = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            treeMap.get(keys.get(random.nextInt(ELEMENT_COUNT)));
        }
        long treeMapGetTime = System.currentTimeMillis() - treeMapGetStart;
        
        // LinkedHashMap性能测试
        long linkedHashMapStart = System.currentTimeMillis();
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            linkedHashMap.put(keys.get(i), values.get(i));
        }
        long linkedHashMapPutTime

更多推荐