上一篇白话HashMap源码(上)看过了HashMap的基本结构和主要的操作方法Get、Put、Remove的源码。了解了HashMap的存储方式,数组加链表,与空间的自增长思路,每次空间翻倍旧数据重新装填。
这一篇主要看一下遍历相关的源码。
遍历前需要了解的方法
containsKey
@Override public boolean containsKey(Object key) {
if (key == null) {//key为null的条件
return entryForNullKey != null;
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {//由key找到数组内的具体项,遍历链表
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return true;
}
}
return false;
}
containsValue
横向遍历数组,纵向遍历链表,如果没有找到别忘了HashMap还维护了一个entryForNullKey。
但是区分了value为null的条件,因为null.equals会报错。
@Override public boolean containsValue(Object value) {
HashMapEntry[] tab = table;
int len = tab.length;
if (value == null) {
for (int i = 0; i < len; i++) {
for (HashMapEntry e = tab[i]; e != null; e = e.next) {
if (e.value == null) {
return true;
}
}
}
return entryForNullKey != null && entryForNullKey.value == null;
}
// value is non-null
for (int i = 0; i < len; i++) {
for (HashMapEntry e = tab[i]; e != null; e = e.next) {
if (value.equals(e.value)) {
return true;
}
}
}
return entryForNullKey != null && value.equals(entryForNullKey.value);
}
遍历keySet
由于keySet、values、entrySet的实现逻辑一样,此处就只看keySet的源码
第一步返回Set<K>
@Override public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet());
}
可以看到返回了一个KeySet对象
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
int oldSize = size;
HashMap.this.remove(o);
return size != oldSize;
}
public void clear() {
HashMap.this.clear();
}
}
里面包含了常用的方法,size方法直接用HashMap对象的size,isEmpty一样,contains则是使用HashMap的containsKey方法,remove也是调用HashMap对象的方法,关键的是iterator这个迭代器。
第二步迭代器
Iterator<K> newKeyIterator() { return new KeyIterator(); }
源码很简单,直接返回KeyIterator对象,继续向下走。
private final class KeyIterator extends HashIterator
implements Iterator<K> {
public K next() { return nextEntry().key; }
}
返回了nextEntry.key,看名字应该是下一项,但nextEntry哪里来的呢?
看KeyIterator 的继承,HashIterator它是整个HashMap迭代的实现者
第三步迭代的实现者
private abstract class HashIterator {
int nextIndex;
HashMapEntry<K, V> nextEntry = entryForNullKey;
HashMapEntry<K, V> lastEntryReturned;
int expectedModCount = modCount;
HashIterator() {//初始时,nextEntry指到第一项
if (nextEntry == null) {
HashMapEntry<K, V>[] tab = table;
HashMapEntry<K, V> next = null;
while (next == null && nextIndex < tab.length) {
next = tab[nextIndex++];
}
nextEntry = next;
}
}
public boolean hasNext() {
return nextEntry != null;
}
HashMapEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == null)
throw new NoSuchElementException();
HashMapEntry<K, V> entryToReturn = nextEntry;
HashMapEntry<K, V>[] tab = table;
HashMapEntry<K, V> next = entryToReturn.next;
//如果这一项是链表,则返回链表的下一项,否则返回数组的下一项
while (next == null && nextIndex < tab.length) {
next = tab[nextIndex++];
}
nextEntry = next;
return lastEntryReturned = entryToReturn;
}
public void remove() {
if (lastEntryReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMap.this.remove(lastEntryReturned.key);
lastEntryReturned = null;
expectedModCount = modCount;
}
}
至此HashMap的源码就看完了,至于其他功能类的方法,相信有了这两篇的基础想看懂原理应该不难。
HashSet的内部实现其实也是HashMap,只是HashSet存入的value作为key,HashSet对象作为value存入HashMap。
网友评论