上一篇分析了HashMap的数据结构以及put方法的源码
HashMap源码解析,下面分析HashMap的遍历过程的源码。
遍历的方法有很多中,主要分析下面这种:
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
String key = next.getKey();
String value = next.getValue();
}
hashMap.entrySet()会new一个EntrySet对象。
调用entrySet.iterator()方法时会通过newEntryIterator()方法调用HashIterator的构造方法创建一个Iterator实例。
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
//..............
}
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
private abstract class HashIterator<E> implements Iterator<E> {
HashMapEntry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
HashMapEntry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
HashIterator的构造方法通过while循环遍历HashMap中的数组,找到第一个不为null的值,并赋值给next。
PS: HashMap中取数据是从数组的第0个位置开始找的,根据上篇分析put方法时,知道插入的位置时根据hash值计算出来的,所以HashMap取出数据的顺序和插入的顺序是不一致的。
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
通过hashMap.entrySet().iterator()已经找到了第一个不为null的元素,如果hasNext()返回false表示没有别的元素,返回true调用iterator.next()方法,此方法最终调用nextEntry()。
nextEntry将构造方法中找到的next返回,并通过next节点中存储的下一个节点的指针去找下个数据,如果下一个数据为null,则表示该链表已经到最尾部了,则继续查找数组下一个位置存储的链表,直到不为null,或者遍历结束。
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//将next 赋值给e 当作返回值使用
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
//链表结构存储了下个节点的指针,可以直接取
if ((next = e.next) == null) {
//当前节点是链表中最后一个元素,从数组下一个位置中查找不为null的元素
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
调用hashMap.get(“4”);取数据,流程和调用put方法保存数据相同,都是通过key的值得到hash值然后找到数组中的位置,之后遍历链表得到Entry。找到Entry之后调用getKey和getValue就非常简单了。
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
这里HashMap的遍历就分析完了。