HashMap keySet与entrySet遍历分析

keySet():

返回的是只存放key值的Set集合,使用迭代器方式遍历该Set集合,在迭代器中再使用get方法获取每一个键对应的值

代码案例:

keySet遍历代码

/**
 * 通过keySet遍历
 * @param map
 */
public static void keySetTest(Map map){
   Iterator keys = map.keySet().iterator();
   while(keys.hasNext()){
       String key = keys.next();
       Object value = map.get(key);
   }
}

源码解析

1.new HashMap().keySet().iterator();

public Set keySet() {
    Set ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}

final class KeySet extends AbstractSet {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator iterator()     { return new KeyIterator(); }
    public final boolean contains(Object o) { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer action) {
        Node[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node e = tab[i]; e != null; e = e.next)
                    action.accept(e.key);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}
final class KeyIterator extends HashIterator
    implements Iterator {
    public final K next() { return nextNode().key; }
}

从上诉源码看出HashMap.keySet().iterator()遍历的 key
 

2.new HashMap().get();

JDK1.7 HashMap 数据结构:数组+链表

JDK1.8 HashMap 数据结构:数组+红黑树(平衡二叉树)

JDK1.7源码解析

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    Entry entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}
final Entry getEntry(Object key) {
    if (size == 0) {
        return null;
    }

    int hash = (key == null) ? 0 : hash(key);
    for (Entry 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;
}

 

JDK1.8源码解析 查找数据走TreeNode分支

public V get(Object key) {
    Node e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

/**
 * Implements Map.get and related methods.
 *
 * @param hash hash for key
 * @param key the key
 * @return the node, or null if none
 */
final Node getNode(int hash, Object key) {
    Node[] tab; Node first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
/**
 * Calls find for root node.
 */
final TreeNode getTreeNode(int h, Object k) {
    return ((parent != null) ? root() : this).find(h, k, null);
}
/**
 * Finds the node starting at root p with the given hash and key.
 * The kc argument caches comparableClassFor(key) upon first use
 * comparing keys.
 */
final TreeNode find(int h, Object k, Class kc) {
    TreeNode p = this;
    do {
        int ph, dir; K pk;
        TreeNode pl = p.left, pr = p.right, q;
        if ((ph = p.hash) > h)
            p = pl;
        else if (ph < h)
            p = pr;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        else if (pl == null)
            p = pr;
        else if (pr == null)
            p = pl;
        else if ((kc != null ||
                  (kc = comparableClassFor(k)) != null) &&
                 (dir = compareComparables(kc, k, pk)) != 0)
            p = (dir < 0) ? pl : pr;
        else if ((q = pr.find(h, k, kc)) != null)
            return q;
        else
            p = pl;
    } while (p != null);
    return null;
}

entrySet()

返回的是存放了映射关系的Set集合(一个映射关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的

entrySet遍历代码

/**
 * 通过entrySet遍历
 * @param map
 */
public static void entrySetTest(Mapmap){
    Iterator> entrys =   map.entrySet().iterator();
    while(entrys.hasNext()){
        Map.Entry entry = entrys.next();
        Object value = entry.getValue();
    }
}

源码接下

1.new HashMap().entrySet().iterator()

public Set> entrySet() {
    Set> es;
    return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

final class EntrySet extends AbstractSet> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator> iterator() {
        return new EntryIterator();
    }
    public final boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry e = (Map.Entry) o;
        Object key = e.getKey();
        Node candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry e = (Map.Entry) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }
    public final Spliterator> spliterator() {
        return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer> action) {
        Node[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node e = tab[i]; e != null; e = e.next)
                    action.accept(e);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}
final class EntryIterator extends HashIterator
    implements Iterator> {
    public final Map.Entry next() { return nextNode(); }
}

从上诉源码看出HashMap.entrySet().iterator()遍历的 数据本身

综上分析:
keySet与entrySet性能差异主要是:使用get方法获取键数据需要遍历数组+链表(红黑树)

测试数据如下(测试数据仅供参考

 JDK1.7环境下 keySet 耗时是 entrySet 约3倍

 JDK1.8环境下 keySet 耗时是 entrySet 约2倍

 JDK1.7,1.8环境下 entrySet性能优化差异不大,略微差于1.8环境

遍历 JDK 1K 10K 100K 1000K 备注
keySet 1.7 9-11ms 16-18ms 45-48ms 313-317ms 遍历50个key,遍历1K,10K,100K,1000K
entrySet 1.7 2-3ms 5-6ms 16-18ms 103-109ms 遍历50个key,遍历1K,10K,100K,1000K
keySet 1.8 9-11ms 11-13ms 34-35ms 195-202ms 遍历50个key,遍历1K,10K,100K,1000K
entrySet 1.8 2-3ms 6-8ms 15-17ms 101-102ms 遍历50个key,遍历1K,10K,100K,1000K

 

你可能感兴趣的:(Java,基础篇,hashmap,数据结构,链表,java)