java容器源码分析(五)——HashMap(续)

只谈情不闲聊 提交于 2019-11-28 10:31:42

续前一篇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方法不太明白。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!