Java中的Map的浅表副本


106

据我了解,有几种方法(可能也有其他方法)Map在Java中创建a的浅表副本:

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();

一种方法优于另一种方法吗?如果是,为什么?

值得一提的是,第二种方式会发出“未经检查的演员表”警告。因此,您必须添加内容@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();
}

在Java的较新版本中(确切地说,自Java 10起),可以使用Map.copyOf静态工厂方法。但是请注意,它返回的地图不可修改!
Oleksandr Pyrohov,

Answers:


106

使用复制构造函数进行复制总是更好。clone()Java中的损坏(请参见SO:如何正确覆盖克隆方法?)。

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

如果您已经阅读了我书中有关克隆的文章,尤其是您在两行之间阅读的话,您会知道我认为它clone已经被深深地打断了。[...]这是一种耻辱Cloneable,但确实发生了。

Bloch(顺便说一句,他设计并实现了Collection框架)甚至说他只提供clone()“因为人们期望它”的方法。他实际上根本不建议使用它。


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


1
是的,这是本书中我最喜欢的部分之一。
polygenelubricants

1
我不想说clone()坏了。我更愿意说克隆是一个糟糕的设计决策,如果使用不当,可能会对您造成很大伤害。另外,您可能永远都不相信别人的clone()方法。因此,我们最终会遇到类似情况,请尝试避免出现这种情况,但它没有损坏。
santiagobasulto

4
使用复制ctor是否不要求您知道要复制的Map的实现?似乎是不必要的限制。
jon-hanson '02

“他只是因为人们期望而只提供了clone()方法” –来源?
亚当·帕金

60

无论是两个:在构造函数中,你指的是对被定义的HashMap实施的地图,(以及其他人),但不是Map接口本身(例如,考虑提供 Map接口的实现:你不会找到该构造函数)。

另一方面,不建议使用该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);

2
因此,请澄清一下:如果您知道要复制Map具有复制构造函数的实现,那么没有理由不使用复制构造函数吗?
亚当·帕金

2
确实,您甚至可以反过来考虑:如果使用putAll则不需要知道Map您使用的实现是否具有复制构造函数。Map因此,任何实现的仅复制构造函数都是多余的。
卡·法乔利

1
可以,尽管通常我比1衬里更喜欢1衬里。;)
亚当·帕金

11

在不知道其实现的情况下复制地图:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}

3
考虑添加<K,V>类型参数以帮助确保类型安全。
巴雷特

1
没有零参数构造函数的地图呢?
艾萨克·萨法德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.