在Java中的地图的浅拷贝

dcp*_*dcp 103 java clone map shallow-copy

据我了解,有几种方法(也许还有其他方法)Map在Java中创建一个浅表副本:

Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy;

// first way
shallowCopy = new HashMap<String, Object>(data);

// second way
shallowCopy = (Map<String, Object>) ((HashMap<String, Object>) data).clone();
Run Code Online (Sandbox Code Playgroud)

一种方式优于另一种方式,如果是这样,为什么?

值得一提的是,第二种方式是"Unchecked Cast"警告.所以你必须添加@SuppressWarnings("unchecked")以解决它,这有点刺激(见下文).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}
Run Code Online (Sandbox Code Playgroud)

pol*_*nts 106

使用复制构造函数进行复制总是更好.clone()在Java中被破坏了(参见SO:如何正确覆盖克隆方法?).

设计中的Josh Bloch - 复制构造函数与克隆

如果你已经在我的书中阅读了关于克隆的项目,特别是如果你在这些内容之间阅读,那么你会知道我认为它clone已经深受打击.[...]这是一个Cloneable被打破的耻辱,但它发生了.

Bloch(顺便说一下,设计并实现了Collection框架)甚至进一步说他只提供了clone()"因为人们期望它"的方法.他实际上并不建议使用它.


我认为更有趣的争论是复制构造函数是否比复制工厂更好,但这是一个完全不同的讨论.

  • 不使用copy ctor要求您知道要复制的Map的哪个实现?似乎是一个不必要的限制. (4认同)

Luc*_*oli 60

两者都不是:你所引用的构造函数是为MapHashMap实现(以及其他)定义的,而不是为Map接口本身定义的(例如,考虑Map接口的Provider实现:你不会找到那个构造函数).

另一方面clone(),如Josh Bloch所解释的那样,不建议使用该方法.

关于Map接口(以及你问的如何复制Map而不是HashMap的问题),你应该使用Map#putAll():

将指定映射中的所有映射复制到此映射(可选操作).对于在指定映射中从键k到值v的每个映射,此调用的效果等同于在此映射上调用put(k,v)的效果.

例:

// HashMap here, but it works for every implementation of the Map interface
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy = new HashMap<String, Object>();

shallowCopy.putAll(data);
Run Code Online (Sandbox Code Playgroud)

  • 那么澄清一下:如果你*知道*正在复制*到*具有复制构造函数的`Map`的实现,那么没有理由不使用复制构造函数吗? (2认同)
  • 确切地说,你甚至可以反过来考虑它:如果你使用`putAll`你不需要知道_你正在使用的`Map`实现是否有复制构造函数.因此,任何"Map"实现的复制构造函数都是多余的. (2认同)

Ter*_*ris 11

复制地图而不知道其实现:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Run Code Online (Sandbox Code Playgroud)

  • 考虑添加"<K,V>"类型参数以帮助确保类型安全. (3认同)