keyset()方法可能更简洁

wwodge7n  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(370)

这个问题在这里已经有答案了

为什么在hashmap.keyset()中声明局部变量ks[重复](2个答案)
上个月关门了。
mac os上的jdk 8,查看hashmap.java中的以下代码:

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

对返回的ks的任何更改都将反映在keyset中,因为它们总是指向相同的基础集,如果这是真的,是否可以写成:

public Set<K> keySet() {
        if (keySet == null) {
            keySet = new KeySet();
        }
        return keySet;
    }

这两个代码段的行为是否相同?
如果是,为什么 HashMap 使用第一个变量而不是第二个变量?

lymnna71

lymnna711#

缓存到局部变量是为了提高性能。生成的字节码更小,字段被读取一次,因此缓存丢失可能只发生一次,以及其他一些事情。
这是一个相当高级的优化,应该只在非常频繁运行的代码段上执行。之所以在这里使用,很可能是因为 HashMap 是用Java1.2编写的,当时jit非常基本,因此类似的东西有相当大的影响。
在这种情况下,这样做也是为了支持多线程访问。 HashMap 不是同步的,但是如果以后不修改它,可以通过安全发布共享。如果两个线程同时执行该方法,则可能会发生争用情况:第一次读入 if(keySet == null) 无法读取另一个线程写入的较新值,而第二个线程已读入 return keySet; 读老一点的( null )价值观。使用局部变量可以确保 if 以及 return 非空时使用相同的引用。所以它再也回不来了 null .

yfwxisqw

yfwxisqw2#

正如@fransesco所指出的,局部变量只保存为优化。在某些情况下,它还可以避免创建新对象。
该实现不在内部存储任何状态,而是在底层hashmap上操作,所有操作和对集合的更改都将按照java文档进行
不允许add和addall(抛出unsupportedoperationexception)
remove、retainall都需要修改基础Map
供参考

/**
     * Returns a {@link Set} view of the keys contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation), the results of
     * the iteration are undefined.  The set supports element removal,
     * which removes the corresponding mapping from the map, via the
     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
     * operations.
     *
     * @return a set view of the keys contained in this map
     */
    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();
            }
        }
    }

所有平台的行为都是一样的,不仅仅是mac

相关问题