更多 Java 集合类方面的文章,请参见文集《Java 集合类》
共同点:
- 都是线程不安全的
- Fail-fast behavior
基本区别:
-
迭代顺序:
- HashMap :遍历时没有特定的顺序,从前到后遍历链表散列(数组 + 链表)。
- LinkedHashMap :遍历时按照插入的顺序或者访问的顺序。
- TreeMap :遍历时按照 key 升序(通过 key 的 compareTo 方法进行比较)。
-
put/remove/get 时间复杂度
- HashMap :O(1)
- LinkedHashMap :O(1)
- TreeMap :O(lgN)
-
是否允许空的 key 和 value
- HashMap :允许空的 key 和 value
- LinkedHashMap :允许空的 key 和 value
- TreeMap :只允许空的 value,key 不能为空
-
实现方式
- HashMap : buckets 链表散列
- LinkedHashMap :double-linked buckets 双向链表散列
- TreeMap :红黑树,迭代时做树的中序遍历
使用 HashMap:
Map m = new HashMap();
m.put("A", "AA");
m.put("C", "CC");
m.put("B", "BB");
m.forEach((k, v) -> System.out.print(k + " "));
打印:A B C
使用 LinkedHashMap:
Map m = new LinkedHashMap();
m.put("A", "AA");
m.put("C", "CC");
m.put("B", "BB");
m.forEach((k, v) -> System.out.print(k + " "));
打印:A C B
使用 TreeMap:
Map m = new TreeMap();
m.put("A", "AA");
m.put("C", "CC");
m.put("B", "BB");
m.forEach((k, v) -> System.out.print(k + " "));
打印:A B C
继承结构
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable {
public class LinkedHashMap
extends HashMap
implements Map
public class TreeMap
extends AbstractMap
implements NavigableMap, Cloneable, java.io.Serializable
LinkedHashMap 的实现
成员变量
LinkedHashMap 采用的 hash 算法和 HashMap 相同,但是它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。
/**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry extends HashMap.Node {
Entry before, after;
Entry(int hash, K key, V value, Node next) {
super(hash, key, value, next);
}
}
初始化
在 LinkedHashMap 的构造方法中,实际调用了父类 HashMap 的相关构造方法来构造一个底层存放的 table 数组,但额外可以增加 accessOrder 这个参数,如果不设置,默认为 false,代表按照插入顺序进行迭代;当然可以显式设置为 true,代表以访问顺序进行迭代。
/**
* Constructs an empty insertion-ordered LinkedHashMap instance
* with the default initial capacity (16) and load factor (0.75).
*/
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
存储
LinkedHashMap 并未重写父类 HashMap 的 put 方法,而是重写了父类 HashMap 的 put 方法调用的子方法void recordAccess(HashMap m) ,void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。
读取
LinkedHashMap 重写了父类 HashMap 的 get 方法,实际在调用父类 getEntry() 方法取得查找的元素后,再判断当排序模式 accessOrder 为 true 时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。
排序模式
LinkedHashMap 定义了排序模式 accessOrder。
- 该属性为 boolean 型变量,对于访问顺序,为 true;
- 对于插入顺序,则为 false;
- 一般情况下,不必指定排序模式,其迭代顺序即为默认为插入顺序。
该哈希映射的迭代顺序就是最后访问其条目的顺序,这种映射很适合构建 LRU 缓存。参考 Java 集合 LinkedHashMap 与 LRU cache
LinkedHashMap 提供了 removeEldestEntry(Map.Entry
方法。
该方法可以提供在每次添加新条目时移除最旧条目的实现程序,默认返回 false,这样,此映射的行为将类似于正常映射,即永远不能移除最旧的元素。
总结
其实 LinkedHashMap 几乎和 HashMap 一样:从技术上来说,不同的是它定义了一个 Entry
LinkedHashMap 通过继承 hashMap 中的 Entry
引用:
LinkedHashMap 的实现原理