概述
HashMap
HashMap存储元素是无序的,在通过Iterator遍历元素时也是无序的。
LinkedHashMap
LinkedHashMap存储元素是无序的,在通过Iterator遍历元素时是有序的;put数据的顺序和输出顺序是一致的。
LinkedHashMap遍历元素的有序性,是采用了双向链表来保存节点。
HashMap源码分析
HashMap数据存储结构
// 主结构 -- 数组
// java.util.HashMap#table
transient Node<K,V>[] table;
// 元素结构之Node
// java.util.HashMap.Node
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
// 元素升级结构之TreeNode(红黑树)
// java.util.HashMap.TreeNode
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
}
Iterator
HashMap<String, String> hashMap = new HashMap<>();
for (int i = 0; i < 6; i++) {
hashMap.put("k-" + i, "v-" + i);
}
// 获取Iterator实例
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
Map.Entry<String, String> entry = null;
while (iterator.hasNext()) {
entry = iterator.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
java.util.HashMap#entrySet
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
java.util.HashMap.EntrySet
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
// 获取Iterator实例
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
// 省略多行代码...
}
java.util.HashMap.EntryIterator
注意,EntryIterator继承于HashIterator,Iterator接口主要实现方法都在abstract HashIterator中
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
java.util.HashMap.HashIterator
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
// 初始化next节点值 -- 注意此处
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
// 判断是否有下一个节点
public final boolean hasNext() {
return next != null;
}
// 获取下一个节点
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next; // 将下一个节点(next节点)赋予e,最后将会返回e节点值
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
// 1. 如果当前节点current.next不为null时,则将 current.next(这是链表上的下一个节点,不是table数组上的元素)存储到成员变量next中
if ((next = (current = e).next) == null && (t = table) != null) {
// 2. current.next == null && table != null
// index从0开始,index是HashIterator的成员变量。
// 当index < table.length && table[index++] == null时,说明该index位置上没有元素,则将会继续循环查找下一个index位置;
// 直至 index >= table.length || table[index++] != null 时,将会退出循环,将结果存储到成员变量next中
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
// 省略多行代码...
}
由nextNode()方法可以看出,HashMap遍历元素时是无序的。
LinkedHashMap源码分析
LinkedHashMap数据存储结构(注意和HashMap数据结构对比)
// 主结构 -- 数组
// java.util.HashMap#table
transient Node<K,V>[] table;
// 元素结构之Entry
// java.util.LinkedHashMap.Entry
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
}
// 元素升级结构之TreeNode(红黑树)
// java.util.HashMap.TreeNode
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
}
Iterator
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
for (int i = 0; i < 6; i++) {
linkedHashMap.put("k-" + i, "v-" + i);
}
// 获取Iterator实例
Iterator<Map.Entry<String, String>> iterator = linkedHashMap.entrySet().iterator();
Map.Entry<String, String> entry = null;
while (iterator.hasNext()) {
entry = iterator.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
java.util.LinkedHashMap#entrySet
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
java.util.LinkedHashMap.LinkedEntrySet
final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new LinkedEntryIterator();
}
// 省略多行代码...
}
java.util.LinkedHashMap.LinkedEntryIterator
注意,LinkedEntryIterator继承于LinkedHashIterator,Iterator接口的主要实现都在abstract LinkedHashIterator中
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
java.util.LinkedHashMap.LinkedHashIterator
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
// 初始化next,将头结点(head)赋予next
next = head;
expectedModCount = modCount;
current = null;
}
// 判断是否有下一个节点
public final boolean hasNext() {
return next != null;
}
// 获取下一个节点
final LinkedHashMap.Entry<K,V> nextNode() {
// 将next赋予e,将e返回
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
// next重新赋予新值,存储当前节点(current = e)的后置节点(after)
next = e.after;
return e;
}
// 省略多行代码...
}
LinkedHashMap是采用了双向链表来保存节点,当在获取下一个节点时,会通过after(后置节点)查找,从而达到遍历的有序性。
接下来在看看LinkedHashMap put的一些源码细节,将会更明白为啥after会获取有序的下一个节点。
LinkedHashMap put
java.util.LinkedHashMap#newNode
/**
* 创建新的节点
* @param hash key的hash值
* @param key
* @param value
* @param e 新添加节点的下一个节点,默认是null
* @return
*/
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;
}
java.util.LinkedHashMap#linkNodeLast
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
// 将新添加的节点p设置为LinkedHashMap的成员变量tail(tail是当前链表的尾节点);
tail = p;
// 尾结点为null时,说明没有任何节点,则将p设置为头结点(head)
if (last == null)
head = p;
//
else {
// 将当前链表的尾结点(last)设置为 新添加节点p的前置节点(before)
p.before = last;
// 在将尾结点(last)的后置节点(after)设置为 新添加节点p
last.after = p;
}
}