续前一篇java容器源码分析(四)——HashMap,继续分析HashMap的源码。
containsValue(Object value):
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
可以看出,这里对table做了一次线性遍历才能够获取出value,复杂度为O(n)。
再看一下Map遍历用到的方法entrySet()
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());
}
entrySet调用了entrySet0,entrySet0返回了EntrySet对象,有点重复的样子!
看来主要的内容在EntrySet中,EntrySet是一个内部类。
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
并没有什么属性!其实它是一个代理类,并且因为它是HashMap的内部类,所以可以直接调用HashMap的方法、属性。这个set并没有add方法。EntrySet的迭代器,是通过newEntryIterator返回。
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
}
继续往下看:
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
EntryIterator是继承了HashIterator类。
private abstract class HashIterator<E> implements Iterator<E>
HashIterator是HashMap的内部抽象类,实现了Iterator接口。
其构造函数
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
遍历table数组,找到第一个不为空的槽。
hasNext方法
public final boolean hasNext() {
return next != null;
}
如果next不为空,则存在下一个元素。
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;
}
remove方法是调用了HashMap的removeEntryForKey方法。没看到next方法,这是因为HashMap想复用HashIteraotr这个类,我们看到HashMap有三个迭代器:
private final class ValueIterator extends HashIterator<V> {
public V next() {
return nextEntry().value;
}
}
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
ValueIterator是给values()用的迭代器,KeyIterator是给KeySet用的迭代器,EntryIterator是提供给EntrySet使用的迭代器。
这里有可以看出一个非常常见的设计:接口实现规范,抽象类实现大部分工作,具体类实现差异化内容!
看下HashIterator的nextEntry方法
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
对table一个槽一个槽的链表遍历。
在看下keySet()方法
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
额,和EntrySet基本一样。继续看Values()
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
private final class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
还是差不多。
从分析Iterator的实现中可以看到,iterator是要遍历整个table的,所以不要将capacity的值设置得太高,也不要把loadfactor的值设置得太低。看HashMap的这句注释:
Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.
HashMap的分析到这里也差不多了,对于HashMap,还是对其hash方法不太明白。