通过Collections.unmodifiable*已经用Collections.unmodifiable*包装的实例是多么低效?

cha*_*ium 4 java collections unmodifiable instanceof map

我有一些计件工作由不同的自定义(源代码不可用)框架完成,这些框架可以传回Map实例.不幸的是,这些框架在返回的Map实例中并不一致,这些实例已用Collections.unmodifiableMap包装.为了确保在我的代码中更高程度的不变性(为了更容易的多线程使用),我在这些框架返回的任何内容上统一调用了Collections.unmodifiableMap.

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);
}
Run Code Online (Sandbox Code Playgroud)

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

似乎使用"instanceof Collections.UnmodifiableMap"不起作用.如果我当前引用的Map实例需要被包装,我找不到任何检测方法(不包括在这种情况下不是选项的反射 - 方式太慢).

问题:

    A)Collections.unmodifiableMap()方法是否检查它是否传递了UnmodifiableMap的实例,如果是这样,只返回相同的引用(从而避免在调用方法之前检查)?
    B)为了主动避免接收修改异常,有没有办法查询Map实例(使用反射除外)来检测它是否可变(或不可变)?
    C)如果A的答案为否,那么JVM/HotSpot是否有一些效率可以消除调用多个方法跳转到核心实例的开销?

Col*_*inD 5

尽我所知:

  • A)不.
  • B)不.
  • C)否

番石榴Immutable*藏品没有这个问题.如果ImmutableList.copyOf(list)使用list它本身是一个ImmutableList调用,则返回参数本身.此外,您可以将它们称为(并检查instanceof)Immutable*类型而不是接口,这样可以很容易地知道您是否具有不可变实例.因此,一种选择是将框架中的结果复制到这些不可变集合中,并在整个代码中使用它们.(它们还具有真正不可变的优点......不可修改的包装器允许它们包装的原始可变实例在有东西引用时自行变异.)

所有这一切,我不会太担心通过1或2个不可修改的包装层传递方法调用的可能开销,只要你不会以某种方式一次又一次地包装它们.正如其他人所指出的那样,由于这个原因,你很可能不会注意到性能问题.


Den*_*hev 5

将一个不可修改的地图包装到另一个地图时,您不必担心性能问题.看看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;
}
    ...
Run Code Online (Sandbox Code Playgroud)

您可以看到此类只是真实地图周围的薄包装.所有方法都一样getSize,isEmpty不影响地图的状态和其他方法都被委托给包裹地图实例.其他影响map状态的方法(put,remove)只是抛出UnsupportedOperationException所以几乎没有性能过载.