美文网首页
HashMap中keySet和entrySet的区别

HashMap中keySet和entrySet的区别

作者: 我爱田Hebe | 来源:发表于2022-12-09 20:03 被阅读0次

    今天进行CR的时候,发现代码中对HashMap进行遍历时,使用的是for(key in map.keys),就想起了之前看到的一个文章,说使用entrySet()进行遍历性能更高。当时没有追究,但是最近看《代码整洁之道》,学到了

    勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)

    那么今天一定要把其中的原理搞清楚。

    用法

    • keySet()
    for (key in map.keys) {
      map[key]
    }
    
    • entrySet()
    for (entry in map.entries) {
      entry.value
    }
    

    差异

    • keySet()返回的是一个关于K的Set (第一次查询),要获取value,还需要通过map[key]获取 (第二步查询)。
    • entrySet()返回的是一个K,V键值对的Set (第一次查询),获取value直接调用entry.value (不需要查询)。

    思考

    1. 虽然使用entrySet()进行遍历,性能更好,但是多线程下就没办法保证安全性了,所以在多线程情况下,建议考虑使用ConcurrentHashMap()

    2. 观察entrySet()方法,发现entrySetnull时,返回的是EntrySet()对象,但是看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(); }
        // 这里返回了EntryIterator()
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry<?, ?> e))
                return false;
            Object key = e.getKey();
            Node<K,V> candidate = getNode(key);
            return candidate != null && candidate.equals(e);
        }
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry<?, ?> e) {
                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 (Node<K,V> e : tab) {
                    for (; e != null; e = e.next)
                        action.accept(e);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }
    

    可以发现iterator()方法返回了EntryIterator()对象,深入其中,发现其继承了HashIterator,而HashIterator构造方法则是从关联到了table,在HashMapput方法中,就是向table中插入值。

    可能有人会问,这与EntrySet()有什么关系呢?for循环怎么获取到的Set对象呢?
    通过对kotlin代码转成Bytecode,再反编译成java语言,可以发现:
    var3 = map.entrySet().iterator();

    while(var3.hasNext()) {
       Map.Entry entry = (Map.Entry)var3.next();
       entry.getValue();
    }
    

    就是说for (entry in map.entries),实际上是遍历的迭代器,这就与前面EntrySetiterator()方法的返回值关联起来了,突然想起了名侦探柯南的画面,哈哈哈

    作者:萌萌的老曹
    链接:https://juejin.cn/post/7174818136456331321

    相关文章

      网友评论

          本文标题:HashMap中keySet和entrySet的区别

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