我有一些分段工作是由不同的自定义(源代码不可用)框架完成的,这些框架可以回送Map实例。不幸的是,这些框架在返回使用Collections.unmodifiableMap包装的Map实例时不一致。为了确保代码中更高程度的不可变性(为了更容易使用多线程),我对这些框架返回的任何内容都统一调用了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);
}我意识到,在我的设计的这一部分,我可能有一个性能问题。在某些情况下,我将Collections.unmodifiableMap称为传递给它的实例,这个实例已经被框架通过同一个调用包装好了。而且我的重新包装很可能导致整个实例中有一个额外的方法调用。
使用"instanceof Collections.UnmodifiableMap“似乎不起作用。而且我找不到任何方法来检测(不包括使用反射,在这种情况下这不是一个选项-太慢了),如果我正在引用的Map实例需要包装或不需要包装。
问题:
( A) Collections.unmodifiableMap()方法是否检查它是否被传递给UnmodifiableMap的一个实例,如果是的话,只返回相同的引用(从而避免在调用方法之前检查)?b)为了主动避免接收修改异常,是否有一种方法可以查询一个Map实例(除了使用反射)来检测它是否是可变的(或不可变的)?如果对A的答案是否定的,那么JVM/热点中是否存在一些效率,从而消除了通过多个方法跳到核心实例进行调用的开销?
发布于 2010-11-09 16:58:18
在回顾了所有的反馈之后,我得出结论,无论我做什么,解决方案都是某种形式的杂念(有轻微的气味)。我认为这是因为Collections中生成不可修改实例的部分没有提供避免嵌套不可修改实例的功能,也没有为客户端提供一种“公共”方式来正确地避免嵌套。
由于考虑到多个类加载器和通过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);
}
}这仍然具有引用比较的所有优点,前提是所有事情都发生在同一个ClassLoader上下文中,并且类名的字符串已经被正确地嵌入。它做到这一点,同时礼貌地保持封装(避免直接引用类名的代码)。但是,如果这两个假设不成立,那么评估将回到一个标准的字符串比较中,如果类名在库的不同版本之间不发生变化(这似乎很低的概率),它就能工作。
在这个方法中,有什么是我忘记或遗漏的吗?
再次感谢各位的反馈。我真的很感激。
发布于 2010-11-08 21:07:24
据我所知:
番石榴的Immutable*集合没有这个问题。如果使用本身为ImmutableList.copyOf(list)的list调用ImmutableList,则返回参数本身。此外,您可以将它们引用为(并与instanceof一起检查) Immutable*类型,而不是接口,这样就可以很容易地知道是否有不可变的实例。因此,一种选择是将框架的结果复制到这些不可变的集合中,并在您自己的代码中使用这些集合。(它们也有一个真正不可改变的优点.不可修改的包装器允许它们包装的原始可变实例在有引用的情况下自身发生变化。)
尽管如此,我并不太担心通过1或2个不可修改的包装层传递方法调用的可能开销,只要您不打算一次又一次地包装它们。正如其他人所指出的,由于这一点,您不太可能注意到性能问题。
发布于 2010-11-08 21:10:35
当您将一个不可修改的映射包装到另一个映射中时,不必担心性能问题。请看一下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和其他不影响映射状态的方法)都委托给包装映射实例。其他影响map状态的方法(put,remove)只抛出UnsupportedOperationException,因此性能重载几乎为零。
https://stackoverflow.com/questions/4127921
复制相似问题