【问题标题】:How to convert Collections.synchronizedMap(new HashMap()) to Hash Map如何将 Collections.synchronizedMap(new HashMap()) 转换为 Hash Map
【发布时间】:2016-01-18 19:35:06
【问题描述】:

我们有一个返回 HashMap 的接口。

现在由于线程安全,我们已经使用同步我们的地图

Collections.synchronizedMap(new HashMap());

现在使用我们接口的客户端面临类转换异常

Collections.synchronizedMap 返回一个同步映射,并且不能转换为哈希映射

我们必须把这个 synchronizedMap 转换回 hash map

有什么方法可以转换

返回的synchronizedMap

Collections.synchronizedMap() 返回哈希映射。

【问题讨论】:

  • 他们可以用new HashMap<>(map)复制它,还是他们想要原件?
  • HashMap<...> map = new HashMap<>(); 随心所欲创建和分发Collections.synchronizedMap(map);,然后,当您需要非同步版本时,只需使用原始map即可。
  • 不确定它是否适用于您的情况,但底线是您的界面应该返回Map,而不是HashMap。你的情况正是Map接口存在的原因。
  • @aioobe 这很危险,因为对于同步映射的任何操作,对 HashMap 的任何访问都不会被排序。即使同步的映射全部是写入,而 HashMap 全部是读取,它仍然是数据竞争。
  • 我试过复制,但同步地图没有复制方法。我想我需要使用 putAll 方法

标签: java collections


【解决方案1】:

如果可以接受复制(也就是说,您不需要后续写入同步映射以反映在返回的映射中),只需使用 HashMap 的constructor that takes a Map input。确保首先在 synchronizedMap 上同步,因为副本需要在地图上进行迭代(无论如何都必须同步)。

synchronized (map) {
  return new HashMap<>(map);
}

同步块并不是真正的可选。来自the docs:

当迭代任何集合视图时,用户必须在返回的地图上手动同步

【讨论】:

    【解决方案2】:

    不完美,但也许它可以在你的情况下工作:创建一个扩展 HashMap 的类,但它内部有一个同步的 Map。然后,将所有方法调用委托给您的内部映射。在构造函数上,创建一个新的 HashMap,然后对其进行同步。像这样的:

    public class SynchronizedHashMap<K, V> extends HashMap<K, V> {
        private Map<K, V> internalMap;
    
        public SynchronizedHashMap(int initialCapacity, float loadFactor) {
            internalMap = Collections.synchronizedMap(new HashMap<>(initialCapacity, loadFactor));
        }
    
        public SynchronizedHashMap(int initialCapacity) {
            internalMap = Collections.synchronizedMap(new HashMap<>(initialCapacity));
        }
    
        public SynchronizedHashMap() {
            internalMap = Collections.synchronizedMap(new HashMap<>());
        }
    
        public SynchronizedHashMap(Map<? extends K, ? extends V> m) {
            internalMap = Collections.synchronizedMap(new HashMap<>(m));
        }
    
        @Override public int size() {
            return internalMap.size();
        }
    
        @Override public boolean isEmpty() {
            return internalMap.isEmpty();
        }
    
        @Override public boolean containsKey(Object o) {
            return internalMap.containsKey(o);
        }
        ...
    }
    

    请记住,这就是您应该为接口而不是具体实现编码的原因。您的界面应该返回一个地图,而您的客户端应该使用一个地图,而不知道它是哪个地图。

    【讨论】:

      【解决方案3】:

      可以吗:

              Map hashMap = new HashMap();
              Map syncMap = Collections.synchronizedMap(hashMap);
              HashMap m = new HashMap(syncMap);
      

      【讨论】:

      • 谢谢大家,你们俩都是正确的,我正在遵循这种方法,
      • 这个实际上是不正确的。它会编译,并且在大部分的时间里都可以工作——但它不是线程安全的,而且你很有可能会遇到错误。更糟糕的是,这将是一个难以重现和发现的错误。众所周知,线程安全错误很难追踪。要做的是小心不要一开始就写它们。
      猜你喜欢
      • 2022-12-05
      • 2015-06-01
      • 2015-01-14
      • 1970-01-01
      • 2012-10-20
      • 2020-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多