美文网首页
HashMap遍历方式性能比较

HashMap遍历方式性能比较

作者: fantasticMao | 来源:发表于2018-03-02 11:56 被阅读355次

    JDK8之前,可以使用ketSet或者entrySet来遍历HashMap,JDK8中引入了map.forEach来进行遍历


    1. keySet和entrySet

    1.1 基本用法

    keySet:

    Map<String,Object> map = new HashMap<>();
    Iterator it = map.keySet().iterator();
    Object key;
    Object value;
    while(it.hasNext()){
        key = it.next();
        value = map.get(key);//性能差异
        System.out.println(key+":"+value);
    }
    

    entrySet:

    Map<String,Object> map = new HashMap<>();
    Iterator it = map.entrySet().iterator();
    Object key;
    Object value;
    while(it.hasNext()){
        Map.Entry entry = (Map.Entry)it.next();
        key = entry.getKey();
        value = entry.getValue();//性能差异
        System.out.println(key+":"+value);
    }
    

    1.2 源码分析

    keySet:

        public Set<K> keySet() {
            Set<K> ks = keySet;
            if (ks == null) {
                ks = new KeySet();
                keySet = ks;
            }
            return ks;
        }
    
        final class KeySet extends AbstractSet<K> {
            public final int size()                 { return size; }
            public final void clear()               { HashMap.this.clear(); }
            public final Iterator<K> 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<K> spliterator() {
                return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
            }
            public final void forEach(Consumer<? super K> action) {
                Node<K,V>[] 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<K,V> e = tab[i]; e != null; e = e.next)
                            action.accept(e.key);
                    }
                    if (modCount != mc)
                        throw new ConcurrentModificationException();
                }
            }
        }
    

    entrySet

        public Set<Map.Entry<K,V>> entrySet() {
            Set<Map.Entry<K,V>> es;
            return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
        }
    
        final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
            public final int size()                 { return size; }
            public final void clear()               { HashMap.this.clear(); }
            public final Iterator<Map.Entry<K,V>> 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<K,V> 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<Map.Entry<K,V>> spliterator() {
                return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
            }
            public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
                Node<K,V>[] 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<K,V> e = tab[i]; e != null; e = e.next)
                            action.accept(e);
                    }
                    if (modCount != mc)
                        throw new ConcurrentModificationException();
                }
            }
        }
    

    从源码中可以看出,当要得到某个value时,keySet需要从hashMap中get,entrySet相比keySet少了遍历table的过程,这就是两者性能上的主要差别

    map.forEach()

    default void forEach(BiConsumer<? super K, ? super V> action) {
            Objects.requireNonNull(action);
            for (Map.Entry<K, V> entry : entrySet()) {
                K k;
                V v;
                try {
                    k = entry.getKey();
                    v = entry.getValue();
                } catch(IllegalStateException ise) {
                    // this usually means the entry is no longer in the map.
                    throw new ConcurrentModificationException(ise);
                }
                action.accept(k, v);
            }
        }
    

    源码分析可知,map.foreach()本质上还是使用的entrySet


    实例

    package com.feiyu.map;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author maokeluo
     * @desc 遍历hashMap性能比较
     * @create 17-9-2
     */
    public class TraverseHsahMap {
    
        public static void main(String[] args) throws InterruptedException {
            Map<String, String> map = new HashMap<String, String>();
            int num = 10000000;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 0; i < num; i++){
                        String temp = String.valueOf(i);
                        map.put(temp,temp);
                    }
                }
            });
            thread.start();
            thread.join();
            
            //forEach map.entrySet
            long start1 = System.currentTimeMillis();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                entry.getKey();
                entry.getValue();
            }
            long end1 = System.currentTimeMillis() - start1;
            System.out.println("forEach map.entrySet遍历HashMap使用时间:"+end1);
    
            //显式调用map.entrySet()的集合迭代器
            long start2 = System.currentTimeMillis();
            Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                entry.getKey();
                entry.getValue();
            }
            long end2 = System.currentTimeMillis() - start2;
            System.out.println("显式调用map.entrySet()的集合迭代器遍历HashMap使用时间:"+end2);
    
            //forEach map.keySet在调用get取值
            long start3 = System.currentTimeMillis();
            for (String key : map.keySet()) {
                map.get(key);
            }
            long end3 = System.currentTimeMillis() - start3;
            System.out.println("forEach map.keySet在调用get取值遍历HashMap使用时间:"+end3);
    
            //forEach map.entrySet(),用临时变量保存map.entrySet()
            long start4 = System.currentTimeMillis();
            Set<Map.Entry<String, String>> entrySet = map.entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                entry.getKey();
                entry.getValue();
            }
            long end4 = System.currentTimeMillis() - start4;
            System.out.println("forEach map.entrySet(),用临时变量保存map.entrySet()使用时间:"+end4);
        }
    }
    
    

    结果

    | map size | 10000 | 100000 | 1000000 | 10000000 |
    |--------|--------|
    | forEach entrySet | 2ms | 11ms | 32ms | 199ms |
    | for iterator entrySet| 1ms | 5ms | 29ms | 203ms |
    | forEach keySet | 2ms | 7ms | 54ms | 273ms |
    | for entrySet=entrySet()| 1ms | 5ms | 28ms | 189ms |

    遍历方式结论总结

    • hashMap的循环遍历,如果不仅需要key又需要value,使用
    for (Map.Entry<String, String> entry : map.entrySet()) {
                entry.getKey();
                entry.getValue();
            }
    

    效率更高

    • 如果只是遍历key不需要value,可以直接使用
    for (String key : map.keySet()) {
                //操作key 
            }
    

    相关文章

      网友评论

          本文标题:HashMap遍历方式性能比较

          本文链接:https://www.haomeiwen.com/subject/zjqixftx.html