LinkedHashMap
在前面,我们了解到了HashMap的使用及原理,HashMap是一种非常常见、非常有用的集合,大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap。不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap插入的顺序,也就是HashMap在遍历时是无序的。 HashMap的这一缺点往往会给我们带来各种困扰,因为有些场景,我们期待一个有序的Map。这个时候,LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。
LinkedHashMap的特点
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
......
}
从继承关系可以看到:LinkedHashMap继承于HashMap类,实现了Map类,因此它的特点与HashMap大致相同,唯一不同点即为插入顺序有序。
关注点 | 结论 |
---|---|
LinkedHashMap是否允许空 | Key 和 Value 都允许空 |
LinkedHashMap是否允许重复数据 | Key重复会进行值覆盖、而Value允许重复 |
LinkedHashMap是否有序 | 有序 |
LinkedHashMap是否线程安全 | 非线程安全 |
LinkedHashMap的存储结构
- LinkedHashMap可以认为是
HashMap+LinkedList
,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。
/**
* The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
/**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
- LinkedHashMap的结点结构在继承于HashMap的基础上,增加了
before
和after
属性来确保插入顺序。并且还维护了头结点head
和尾结点tail
。在插入数据时,不但需要通过哈希算法进行存储,还需要通过before
和after
模拟双向链表存储结构,进行插入顺序的维护。
LinkedHashMap所继承于HashMap结点中的
next
属性是用于维护HashMap中table数组中存储的链表。而其独有的before
和after
是模拟双向链表进行结点插入顺序的维护。
LinkedHashMap的使用
LinkedHashMap的使用方法与HashMap完全一致,不同点在于LinkedHashMap重写了HashMap中新增结点的方法,用于维护其插入顺序。
- 1、从table角度来看,新的Entry节点插入到数组对应的下标里,当有哈希冲突时,采用头插法将新的Entry插入到冲突链表的头部。
- 2、从头结点head的角度来看,新的Entry节点插入到双向链表的尾部。
/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and
* removes the eldest entry if appropriate.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
/**
* This override differs from addEntry in that it doesn't resize the
* table or remove the eldest entry.
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header); //双向链表的头
size++;
}
附上一个我认为总结比较完善的博客链接:LinkedHashMap
TreeMap
在前面我们通过HashMap中插入顺序无序引出了LinkedHashMap的使用,但是我们又可以发现,这两种存储方式在迭代时均不是按照数据的大小顺序进行遍历的,而当我们需要将数据按照大小顺序迭代时,就需要此时的TreeMap集合了。
- TreeMap是一个大小有序的key-value集合,底层结构是红黑树,不允许插入null值。
- TreeMap采用红黑树的插入和删除方法,通过比较key决定新元素的插入位置,也通过红黑树的有序性质进行删除。
- TreeMap需要通过Comparable或Comparator进行元素的排序。
TreeMap的特点
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
......
}
- 1、TreeMap
是一个有序的key-value集合
,它是通过红黑树实现的。 - 2、TreeMap 继承于AbstractMap,