Java Map深入:HashMap、TreeMap与ConcurrentHashMap
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. 最佳实践
- 选择合适的Map:
- 需要快速查找:HashMap
- 需要排序:TreeMap
- 需要保持插入顺序:LinkedHashMap
- 需要线程安全:ConcurrentHashMap
- 使用恰当的初始容量:避免频繁扩容
- 使用compute/merge:原子操作比get+put更安全
- 注意null键值:HashMap允许null,ConcurrentHashMap不允许
- 合理使用并发集合:多线程环境使用ConcurrentHashMap
总结
Map是Java集合框架中最重要的数据结构之一。掌握不同Map实现的特点和使用场景,可以高效地处理各种键值对数据。在实际编程中,要根据需求选择合适的Map实现,并注意性能和线程安全。