← 返回首页
🗺️

Java Map深入:HashMap、TreeMap与ConcurrentHashMap

📂 java ⏱ 5 min 851 words

Java Map深入:HashMap、TreeMap与ConcurrentHashMap

概述

Map是Java集合框架中用于存储键值对的数据结构。Java提供了多种Map实现,包括HashMap、TreeMap、LinkedHashMap和ConcurrentHashMap等。

1. HashMap

基本操作

import java.util.*;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建HashMap
        Map<String, Integer> scores = new HashMap<>();
        
        // 添加键值对
        scores.put("Alice", 85);
        scores.put("Bob", 92);
        scores.put("Charlie", 78);
        scores.put("David", 88);
        
        System.out.println("Map: " + scores);
        
        // 访问元素
        System.out.println("Alice: " + scores.get("Alice"));
        System.out.println("Eve: " + scores.getOrDefault("Eve", 0));
        
        // 修改元素
        scores.put("Alice", 90);
        System.out.println("修改后Alice: " + scores.get("Alice"));
        
        // 删除元素
        scores.remove("Bob");
        System.out.println("删除Bob后: " + scores);
        
        // 检查键值
        System.out.println("包含Charlie: " + scores.containsKey("Charlie"));
        System.out.println("包含92: " + scores.containsValue(92));
        
        // 遍历
        System.out.println("遍历:");
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 计算操作
        scores.merge("Alice", 5, Integer::sum);
        System.out.println("merge后Alice: " + scores.get("Alice"));
        
        scores.computeIfAbsent("Eve", k -> 100);
        System.out.println("computeIfAbsent后Eve: " + scores.get("Eve"));
        
        scores.computeIfPresent("Alice", (k, v) -> v + 10);
        System.out.println("computeIfPresent后Alice: " + scores.get("Alice"));
    }
}

HashMap原理

import java.util.*;

public class HashMapPrinciple {
    public static void main(String[] args) {
        // HashMap底层是数组+链表+红黑树
        // 默认初始容量:16
        // 默认负载因子:0.75
        // 链表长度超过8转红黑树
        
        // 自定义容量和负载因子
        Map<String, Integer> map = new HashMap<>(32, 0.5f);
        
        // 计算hash值
        String key = "Hello";
        int hash = key.hashCode();
        System.out.println("Hash: " + hash);
        System.out.println("Hash & 15: " + (hash & 15));  // 确定数组位置
        
        // 扩容机制
        // 当元素数量超过 容量*负载因子 时,会进行扩容
        // 扩容为原来的2倍
    }
}

2. TreeMap

基本操作

import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        // 创建TreeMap(按键排序)
        Map<String, Integer> scores = new TreeMap<>();
        
        scores.put("Charlie", 78);
        scores.put("Alice", 85);
        scores.put("Bob", 92);
        scores.put("David", 88);
        
        System.out.println("TreeMap: " + scores);
        
        // 自然排序(按键的compareTo方法)
        // TreeMap会自动按键排序
        
        // 自定义排序
        Map<String, Integer> customSorted = new TreeMap<>(Comparator.reverseOrder());
        customSorted.put("Charlie", 78);
        customSorted.put("Alice", 85);
        customSorted.put("Bob", 92);
        
        System.out.println("逆序排序: " + customSorted);
        
        // 范围查询
        TreeMap<String, Integer> treeMap = new TreeMap<>(scores);
        
        System.out.println("第一个: " + treeMap.firstKey());
        System.out.println("最后一个: " + treeMap.lastKey());
        System.out.println("Alice之前: " + treeMap.headMap("Alice"));
        System.out.println("Alice之后: " + treeMap.tailMap("Alice"));
        System.out.println("Bob到David: " + treeMap.subMap("Bob", "E"));
    }
}

3. LinkedHashMap

import java.util.*;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        // 创建LinkedHashMap(保持插入顺序)
        Map<String, Integer> scores = new LinkedHashMap<>();
        
        scores.put("Charlie", 78);
        scores.put("Alice", 85);
        scores.put("Bob", 92);
        
        System.out.println("LinkedHashMap: " + scores);
        
        // 访问顺序(用于LRU缓存)
        Map<String, Integer> accessOrder = new LinkedHashMap<>(16, 0.75f, true);
        accessOrder.put("A", 1);
        accessOrder.put("B", 2);
        accessOrder.put("C", 3);
        
        System.out.println("初始: " + accessOrder);
        
        accessOrder.get("A");  // 访问A
        System.out.println("访问A后: " + accessOrder);
        
        // 实现LRU缓存
        Map<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
                return size() > 3;  // 容量超过3时移除最久未使用的
            }
        };
        
        lruCache.put("A", 1);
        lruCache.put("B", 2);
        lruCache.put("C", 3);
        System.out.println("LRU初始: " + lruCache);
        
        lruCache.get("A");  // 访问A
        lruCache.put("D", 4);  // 添加D,移除最久未使用的B
        System.out.println("LRU操作后: " + lruCache);
    }
}

4. ConcurrentHashMap

线程安全Map

import java.util.concurrent.*;
import java.util.*;

public class ConcurrentHashMapExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建ConcurrentHashMap
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        // 基本操作
        map.put("Alice", 85);
        map.put("Bob", 92);
        map.put("Charlie", 78);
        
        System.out.println("Map: " + map);
        
        // 原子操作
        map.putIfAbsent("David", 88);
        System.out.println("putIfAbsent后: " + map);
        
        map.computeIfAbsent("Eve", k -> 100);
        System.out.println("computeIfAbsent后: " + map);
        
        map.merge("Alice", 5, Integer::sum);
        System.out.println("merge后Alice: " + map.get("Alice"));
        
        // 并发测试
        ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        int threadCount = 10;
        int incrementsPerThread = 1000;
        
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < incrementsPerThread; j++) {
                    concurrentMap.merge("counter", 1, Integer::sum);
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("并发计数: " + concurrentMap.get("counter"));
        System.out.println("期望值: " + (threadCount * incrementsPerThread));
    }
}

并发遍历

import java.util.concurrent.*;
import java.util.*;

public class ConcurrentIteration {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        for (int i = 0; i < 100; i++) {
            map.put("key" + i, i);
        }
        
        // 并发遍历
        System.out.println("并行遍历:");
        map.entrySet().parallelStream()
            .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
        
        // 搜索
        String foundKey = map.search(10, (key, value) -> 
            value > 50 ? key : null);
        System.out.println("找到key: " + foundKey);
        
        // 归约
        int sum = map.reduceValues(10, Integer::sum);
        System.out.println("值总和: " + sum);
        
        // 计数
        long count = map.mappingCount();
        System.out.println("元素数量: " + count);
    }
}

5. 实际应用示例

单词频率统计

import java.util.*;

public class WordFrequencyCounter {
    public static Map<String, Integer> countWords(String text) {
        Map<String, Integer> frequencyMap = new HashMap<>();
        
        String[] words = text.toLowerCase().split("\\s+");
        
        for (String word : words) {
            word = word.replaceAll("[^a-zA-Z]", "");
            if (!word.isEmpty()) {
                frequencyMap.merge(word, 1, Integer::sum);
            }
        }
        
        return frequencyMap;
    }
    
    public static void main(String[] args) {
        String text = "Hello World Hello Java Hello World";
        
        Map<String, Integer> frequency = countWords(text);
        
        System.out.println("单词频率:");
        frequency.entrySet().stream()
            .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
            .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
    }
}

缓存实现

import java.util.concurrent.*;
import java.util.*;

public class Cache<K, V> {
    private final Map<K, V> cache;
    private final Map<K, Long> timestamps;
    private final long ttl;  // 生存时间(毫秒)
    
    public Cache(int capacity, long ttl) {
        this.cache = new ConcurrentHashMap<>();
        this.timestamps = new ConcurrentHashMap<>();
        this.ttl = ttl;
    }
    
    public void put(K key, V value) {
        cache.put(key, value);
        timestamps.put(key, System.currentTimeMillis());
    }
    
    public V get(K key) {
        Long timestamp = timestamps.get(key);
        if (timestamp == null) {
            return null;
        }
        
        if (System.currentTimeMillis() - timestamp > ttl) {
            cache.remove(key);
            timestamps.remove(key);
            return null;
        }
        
        return cache.get(key);
    }
    
    public void remove(K key) {
        cache.remove(key);
        timestamps.remove(key);
    }
    
    public int size() {
        return cache.size();
    }
    
    public static void main(String[] args) throws InterruptedException {
        Cache<String, String> cache = new Cache<>(100, 1000);  // 1秒过期
        
        cache.put("key1", "value1");
        cache.put("key2", "value2");
        
        System.out.println("key1: " + cache.get("key1"));
        System.out.println("key2: " + cache.get("key2"));
        
        Thread.sleep(1500);  // 等待1.5秒
        
        System.out.println("过期后key1: " + cache.get("key1"));
        System.out.println("过期后key2: " + cache.get("key2"));
    }
}

6. 最佳实践

  1. 选择合适的Map
    • 需要快速查找:HashMap
    • 需要排序:TreeMap
    • 需要保持插入顺序:LinkedHashMap
    • 需要线程安全:ConcurrentHashMap
  2. 使用恰当的初始容量:避免频繁扩容
  3. 使用compute/merge:原子操作比get+put更安全
  4. 注意null键值:HashMap允许null,ConcurrentHashMap不允许
  5. 合理使用并发集合:多线程环境使用ConcurrentHashMap

总结

Map是Java集合框架中最重要的数据结构之一。掌握不同Map实现的特点和使用场景,可以高效地处理各种键值对数据。在实际编程中,要根据需求选择合适的Map实现,并注意性能和线程安全。