两万字长文读懂 Java 集合( 四 )


关于自然排序与定制排序:

  • 自然排序:要求 key 必须实现 Comparable 接口 。
由于 Integer 类实现了 Comparable 接口,按照自然排序规则是按照 key 从小到大排序 。
 TreeMap<Integer, String> treeMap = new TreeMap<>;treeMap.put(2, "TWO");treeMap.put(1, "ONE");System.out.print(treeMap);// {1=ONE, 2=TWO}
  • 定制排序:在初始化 TreeMap 时传入新的 Comparator,不要求 key
    实现 Comparable 接口
 TreeMap<Integer, String> treeMap = new TreeMap<>((o1, o2) -> Integer.compare(o2, o1));treeMap.put(1, "ONE");treeMap.put(2, "TWO");treeMap.put(4, "FOUR");treeMap.put(3, "THREE");System.out.println(treeMap);// {4=FOUR, 3=THREE, 2=TWO, 1=ONE}通过传入新的 Comparator 比较器,可以覆盖默认的排序规则,上面的代码按
照key降序排序,在实际应用中还可以按照其它规则自定义排序 。
compare 方法的返回值有三种,分别是:0,-1,+1
(1)如果返回0,代表两个元素相等,不需要调换顺序
(2)如果返回+1,代表前面的元素需要与后面的元素调换位置
(3)如果返回-1,代表前面的元素不需要与后面的元素调换位置
而何时返回+1和-1,则由我们自己去定义,JDK默认是按照自然排序,而我们可以根据key的不同去定义降序还是升序排序 。
关于 TreeMap 主要介绍了两点:
  1. 它底层是由红黑树这种数据结构实现的,所以操作的时间复杂度恒为O(logN)
  2. TreeMap 可以对 key 进行自然排序或者自定义排序,自定义排序时需要传入 Comparator,而自然排序要求key实现了 Comparable 接口
  3. TreeMap 不是线程安全的 。
 
WeakHashMapWeakHashMap 日常开发中比较少见,它是基于普通的 Map 实现的,而里面 Entry 中的键在每一次的垃圾回收都会被清除掉,所以非常适合用于短暂访问、仅访问一次的元素,缓存在 WeakHashMap 中,并尽早地把它回收掉 。
当 Entry 被 GC 时,WeakHashMap 是如何感知到某个元素被回收的呢?
在 WeakHashMap 内部维护了一个引用队列 queue
 private final ReferenceQueue<Object> queue = new ReferenceQueue<>;这个 queue 里包含了所有被 GC 掉的键,当 JVM 开启 GC 后,如果回收掉
WeakHashMap 中的 key,会将 key 放入 queue 中,在 expungeStaleEntr
ies 中遍历 queue,把 queue 中的所有 key 拿出来,并在 WeakHashMap
删除掉,以达到同步 。
 private void expungeStaleEntries {for (Object x; (x = queue.poll) != ; ) {synchronized (queue) {// 去 WeakHashMap 中删除该键值对}}}再者,需要注意 WeakHashMap 底层存储的元素的数据结构是数组 + 链表,没
有红黑树哦,可以换一个角度想,如果还有红黑树,那干脆直接继承 HashMap,
然后再扩展就完事了嘛,然而它并没有这样做:
 public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {}所以,WeakHashMap 的数据结构图我也为你准备好啦 。
两万字长文读懂 Java 集合

文章插图
图中被虚线标识的元素将会在下一次访问 WeakHashMap 时被删除掉,WeakHashMap 内部会做好一系列的调整工作,所以记住队列的作用就是标志那些已经被 GC 回收掉的元素 。
关于 WeakHashMap 需要注意两点:
  1. 它的键是一种弱键,放入 WeakHashMap 时,随时会被回收掉,所以不能确保某次访问元素一定存在
  2. 它依赖普通的 Map 进行实现,是一个非线程安全的集合
  3. WeakHashMap 通常作为缓存使用,适合存储那些只需访问一次、或只需保存短暂时间的键值对
 
HashtableHashtable 底层的存储结构是数组 + 链表,而它是一个线程安全的集合,但是因为这个线程安全,它就被淘汰掉了 。
下面是 Hashtable 存储元素时的数据结构图,它只会存在数组+链表,当链表过长时,查询的效率过低,而且会长时间锁住 Hashtable 。
两万字长文读懂 Java 集合

文章插图
这幅图是否有点眼熟哈哈哈哈,本质上就是 WeakHashMap 的底层存储结构了 。你千万别问为什么 WeakHashMap 不继承 Hashtable 哦,Hashtable 的性能在并发环境下非常差,在非并发环境下可以用 HashMap 更优 。
HashTable 本质上是 HashMap 的前辈,它被淘汰的原因也主要因为两个字:性能


推荐阅读