代码之家  ›  专栏  ›  技术社区  ›  chaotic3quilibrium

传递集合的效率有多低。不可修改*已用集合包装的实例。不可修改*?

  •  4
  • chaotic3quilibrium  · 技术社区  · 15 年前

    Map<String, Record> immutableMap = framework.getRecordsByName();
    //does this created a nested set of unmodifiableMap wrapper instances?
    this.immutableField = Collections.unmodifiableMap(immutableMap);
    .
    .
    .
    Map<String, Record> maybeImmutableMap = framework.getRecordsByName();
    //is there some means to get instanceof to work?
    if (!(maybeImmutableMap instanceof Collections.UnmodifiableMap))
    {
        this.immutableField = Collections.unmodifiableMap(maybeImmutableMap);
    }
    

    我意识到我的设计中的这一部分可能会有性能问题。在某些情况下,我调用Collections.unmodifiableMap,传递给它一个实例,该实例已经由框架通过同一个调用包装。而且我的重新包装很可能会在整个实例中引起一个额外的方法调用。

    似乎使用“instanceof Collections.UnmodifiableMap”不起作用。如果我当前引用的映射实例需要包装或不需要包装,我找不到任何方法来检测(除了使用反射,反射在这种情况下不是一个选项-太慢)。

    问题:

      A) Collections.unmodifiableMap()方法是否检查是否向其传递了unmodifiableMap的实例,如果是,则返回相同的引用(从而避免在调用方法之前进行检查)?
      B) 为了主动避免接收修改异常,是否有方法查询映射实例(而不是使用反射)以检测其是否可变(或不可变)?
    6 回复  |  直到 15 年前
        1
  •  5
  •   ColinD    15 年前

    据我所知:

    • A) 没有。
    • B) 没有。

    Guava Immutable* 收藏没有这个问题。如果 ImmutableList.copyOf(list) list 这本身就是 ImmutableList ,则返回参数本身。另外,您可以将它们称为 instanceof 对于 不变的* 输入而不是接口,这样就很容易知道是否有不可变的实例。因此,一种选择是将框架中的结果复制到这些不可变的集合中,并在您自己的代码中使用这些集合。(它们还有一个优势,就是真正的不可改变。。。不可修改的包装器允许它们包装的原始可变实例在有引用的情况下进行自身的修改。)

        2
  •  5
  •   Denys Kniazhev-Support Ukraine    12 年前

    当您将一个不可修改的映射包装到另一个映射时,不必担心性能问题。看一看 UnmodifiableMap 班级:

    private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
        ...
    UnmodifiableMap(Map<? extends K, ? extends V> m) {
            if (m==null)
                throw new NullPointerException();
            this.m = m;
        }
    
    public int size()                {return m.size();}
        ...
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
        }
    public V remove(Object key) {
        throw new UnsupportedOperationException();
        }
    
    public Set<K> keySet() {
        if (keySet==null)
        keySet = unmodifiableSet(m.keySet());
        return keySet;
    }
    
    public Set<Map.Entry<K,V>> entrySet() {
        if (entrySet==null)
        entrySet = new UnmodifiableEntrySet<K,V>(m.entrySet());
        return entrySet;
    }
        ...
    

    getSize , isEmpty 其他不影响映射状态的方法被委托给包装的映射实例。影响地图状态的其他方法( put , remove UnsupportedOperationException 所以几乎没有性能过载。

        3
  •  2
  •   Jörn Horstmann    15 年前

    我对(C)的看法是,hotspot编译器应该非常擅长内联只返回另一个方法调用结果的小方法。但我怀疑它是否能看穿几个级别的间接寻址,例如一个HashMap包在几个不可修改的映射中。如果你知道你的库有时会返回不可修改的地图,我也不认为这是过早的优化。为什么不为Collections.unmodifiableMap创建自己的包装器(未经测试,我刚刚注意到一个简单的instanceof不会工作,因为Collections中的Impl是私有的):

    public class MyCollections {
        private static final Class UNMODIFIABLE_MAP_CLASS = Collections.unmodifiableMap(new HashMap()).getClass();
    
        public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) {
            return map.getClass() == UNMODIFIABLE_MAP_CLASS ? map : Collections.unmodifiableMap(map);
        }
    }
    
        4
  •  2
  •   chaotic3quilibrium    15 年前

    由于考虑到多个类装入器和通过RMI的序列化,我真正喜欢的一个解决方案(Jorn Horstmann的类引用比较)有问题。然而,当我采用他的方法并将其与类名方法的修改(由Eugene Kuleshov推荐)相结合时,我想我已经接近了能够在多线程分布式处理环境中帮助我的解决方案。有点像这样:

    public class MyCollections {
        private static final String UNMODIFIABLE_MAP_CLASS_NAME =
            Collections.unmodifiableMap(new HashMap()).getClass().getName();
    
        public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) {
            return map.getClass().getName().equals(UNMODIFIABLE_MAP_CLASS_NAME)
                     ? map
                     : Collections.unmodifiableMap(map);
        }
    }
    

    这仍然具有参考比较的所有优点 假设 类名的字符串已被正确地存储。它在做到这一点的同时,礼貌地保持封装(避免我的代码直接引用类名)。但是,如果这两个假设不成立,那么计算将返回到标准字符串比较,如果类名在库的不同版本之间没有变化(这似乎是一个很低的概率),则标准字符串比较将起作用。

        5
  •  0
  •   Robin    15 年前

    A) 没有

    B) 没有

    C) 可能吧,但我希望这取决于JVM的实现。

    所讨论的decorator非常轻量级,是否有某种特定的原因导致只进行另一个方法调用的额外方法调用会在您的环境中导致一些性能问题?这在任何标准应用程序中都不应该是一个问题,所以除非您有一些真正特殊的应用程序/环境(cpu密集型、实时、移动设备…),否则应该不是问题。

        6
  •  0
  •   Eugene Kuleshov    15 年前

    一般来说,第一条规则是除非确实有必要,否则不要优化它。基本上,您需要从应用程序中收集一些性能数据,以查看包装的收集是否会导致性能下降。

    推荐文章