Java Map家族全面解析:从基础到高阶应用

64 阅读4分钟

Map家族

1、顶层接口:Map<K,V>

  • 实现:定义了 键值对的存储 的抽象接口
    • 常用方法:put() get() remove() containsKey() entrySet() keySet() values()
  • 底层:只是规范,并不关心具体数据结构
  • 应用:统一API,让不同实现类有相同的使用方式
  • 作用:抽象层,解耦调用方与实现类

2、HashMap

  • 底层:

    • jdk 1.7:数组+链表 jdk1.8+:数组+链表+红黑树(后链表长度 > 8 时转为红黑树)
    • 哈希函数:对hashCode做扰动运算,减少冲突
    • 扩容:容量翻倍,重新hash
  • 应用:缓存、统计、配置存储

  • 作用:O(1)平均查找、插入效率 无序查找,效率优先

  • 业务场景(最常用的):

    1. 缓存:存储一些临时数据,比如一个用户的ID和用户信息的映射
    Map<Integer, User> userCache = new HashMap();
    userCache.put(1001, new User("wjj"));
    
    1. 快速查找:比如统计单词出现的次数
    Map<String, Integer> countMap = HashMap<>();
    // 统计 apple 出现的次数,出现就+1,否则放入1
    
    1. 配置存储:存储一些配置项,比如数据库连接参数(key=配置名,value=值)

3、LinkedHashMap

  • 底层:继承HashMap,多了一个 双向链表,保证插入顺序或访问顺序

  • 特点:有序(插入顺序 或 LRU顺序)

  • 业务场景:

    1. LRU缓存(最常见):可以设置accessOrder = true ,自动把最近访问的放到最后,淘汰最久没有访问的 (accessOrder 按照访问顺序排列 false就是按照插入顺序)
    LinkedHashMap<Integer, String> lruCache = new LinkedHashMap<>(16, 0.75f, true);
    // 第一个参数:初始容量 第二个:负载因子(当元素超过16*0.75=12时进行扩容)
    // 第三个参数:是否按照访问顺序排序
    
    1. 需要顺序输出的情况:比如保存菜单项,按用户定义的顺序展示

4、TreeMap

  • 底层:红黑树(有序二叉查找树)

  • 特点:按照 key 排序(自然排序 or Comparator)。查找 / 插入O(logN)

  • 业务场景:

    1. 排行榜:比如存储用户分数,key = 分数,value = 用户,天然排序
    TreeMap<Integer, String> rank = new TreeMap<>();
    rank.put(100, "wjj");
    rank.put(200, "jjw");	// TreeMap会自动进行排序
    
    1. 范围查询:比如找“分数在80-100的所有学生”
    SortedMap<Integer, String> sub = rank.subMap(80, 101);
    /*
    	TreeMap就是实现了SortedMap接口的具体实现类
    	 SortedMap是一个接口,规定 有序Map 应该支持的一些功能
    	1. subMap(fromKey, toKey)	获取某个范围的子视图
    	2. headMap(toKey) 	获取比某个key小的所有映射
    	3. tailMap()	获取比某个key大的所有映射
    	4. firstKey()、lastKey()		获取最大、最小key
    */
    

5、ConcurrentHashMap

为什么使用这个?

  1. HashMap线程不安全,多线程环境下会出现问题(eg:死循环、数据丢失)
  2. Hashtable虽然线程安全,但是因为对方加了全表锁(synchronized),并发性能很差
  • 底层:分段锁(jdk7),CAS+synchronized(jdk8)

  • 特点:高并发环境下使用,性能远高于Hashtable,其迭代器是弱一致性,其他线程更改Map,不会抛异常

  • 业务场景:

    1. 并发缓存:多线程同时读写缓存数据(用户session、token、临时数据啥的)
    ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
    cache.put("token", "xyz123");
    
    1. 统计并发管理访问量:比如电商系统中统计商品的访问次数
    2. 存放共享资源:比如多个线程共享的用户session信息

6、Hashteble

  • 底层:数组+链表
  • 特点:线程安全(方法上加synchronized,全),不允许null key和null value
  • 业务场景:
    1. 早期多线程程序:现在基本被ConcurrentHashMap替代
    2. 维护老项目会遇到,新开发基本用不上

7、WeakHashMap

  • 底层:key用弱引用存储,GC发现key没有强引用时,会自动回收对应的entry
  • 特点:适合做“临时性缓存”
  • 业务场景:
    1. 缓存场景:避免缓存导致内存泄露,比如class loader缓存(当key没人引用时,GC会自动清理)
    2. 图片缓存:android中用过,用于图片对象的缓存

8、IdentityHashMap

  • 底层:和HashMap类似,但key比较用 ==(地址比较) 而不是equals()

  • 业务场景:

    1. 对象唯一性映射:比如序列化时,判断一个对象是否已经被处理过
    // 普通 HashMap 会认为两个内容一样的对象是同一个,但 IdentityHashMap 可以区分
    String a = new String("abc");
    String b = new String("abc");
    Map<String, String> map = new IndentityHashMap<>();
    map.put(a, "A");
    map.put(b, "B");
    System.out.println(map.size())	// 输出2		而 HashMap 会输出 1
    
    1. 特殊框架实现:一般业务开发几乎不用