LinkedHashMap
LinkedHashMap
在java中,所有加了Link前缀的数据结构,都是可以按照顺序进行访问的,例如LinkedHashMap。LinkedHashMap和HashMap之间的区别在于它使用了一个双向链表将所有的entries链接起来。这个双向链表默认是按照entry的插入先后顺序构成的,如果重复插入一个key值相同的entry是不会影响链表的顺序的哦。
LinkedHashMap也不是线程安全的,如果想要在多线程环境中使用,应该在构造的时候使用如下的语句:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
LinkedHashMap也是fail-fast
的,如果在迭代过程中,LinkedHashMap的结构发生了变化,就会抛出ConcurrentModificationException
异常。
因为HashMap现在已经改为链表和红黑树两种存储方式了,LinkedHashMap作为HashMap的子类,也改变了自己的存储方式,所以LinkedHashMap不是按照链表来存储的。
使用
- 默认按照插入顺序排序
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapTest {
public static void main(String[] args) {
Map<String, Integer> test_map = new LinkedHashMap<String, Integer>();
// Map<String, Integer> test_map = new LinkedHashMap<String, Integer>(20, 0.7F, true);
test_map.put("人类", 1);
test_map.put("泰坦", 10);
test_map.put("兽人", 2);
test_map.put("精灵", 3);
test_map.get("兽人");
Iterator iter = test_map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
输出:
人类 : 1
泰坦 : 10
兽人 : 2
精灵 : 3
上面的输出是按照输入的顺序来的,如果将LinkedHashMap变为HashMap就会发现输出的是一个无序的顺序了。
- LinkedHashMap还有一种排序方式,类似于LRU,最近最久未使用,会将最不常用的entry排在最前。
public class LinkedHashMapTest {
public static void main(String[] args) {
// Map<String, Integer> test_map = new HashMap<String, Integer>();
Map<String, Integer> test_map = new LinkedHashMap<String, Integer>(20, 0.7F, true);
test_map.put("人类", 1);
test_map.put("泰坦", 10);
test_map.put("兽人", 2);
test_map.put("精灵", 3);
test_map.get("兽人");
test_map.get("人类");
Iterator iter = test_map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
输出:
泰坦 : 10
精灵 : 3
兽人 : 2
人类 : 1
实现介绍
实现方面,LinkedHashMap是在HashMap的基础上增加了对列表的操作。
- Entry
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);
}
}
Entry的结构如上,每个Entry多了一个before 和 after 变量,分别指向链表前后的节点。
- 构造函数
LinkedHashMap的构造函数中有一个参数可以控制是否按照访问顺序进行排序,不过只有下面这个构造函数才可以使用,
也就是必须同时传三个参数才可以。
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
- 将entry添加到链表中
LinkedHashMap并不是在PUT函数中将entry添加到链表中,而是在新建entry(Node)的时候,添加到链表中。
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
linkNodeLast(p);
return p;
}
- 那么这个访问顺序修改是怎么实现的呢,它是在
get
函数中修改了链表的结构。
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
//如果accessOrder为真,就修改链表的顺序,将它放到链表的最后
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
//如果这个节点不是最后的节点,就将该节点放到最后
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
posted on 2016-06-11 13:39 walkwalkwalk 阅读(231) 评论(0) 编辑 收藏 举报