108

As I understand it, there are a couple of ways (maybe others as well) to create a shallow copy of a Map in 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();

Is one way preferred over the other, and if so, why?

One thing worth mentioning is that the second way gives an "Unchecked Cast" warning. So you have to add @SuppressWarnings("unchecked") to get around it, which is a little irritating (see below).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}
polygenelubricants
  • 348,637
  • 121
  • 546
  • 611
dcp
  • 51,027
  • 19
  • 136
  • 157
  • 1
    In the newer versions of Java (since Java 10, to be exact) you can use the [Map.copyOf](https://docs.oracle.com/javase/10/docs/api/java/util/Map.html#copyOf(java.util.Map)) static factory method. But note that it returns an unmodifiable Map! – Oleksandr Pyrohov Jun 07 '19 at 18:29

3 Answers3

106

It's always better to copy using a copy constructor. clone() in Java is broken (see SO: How to properly override clone method?).

Josh Bloch on Design - Copy Constructor versus Cloning

If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. [...] It's a shame that Cloneable is broken, but it happens.

Bloch (who by the way, designed and implemented the Collection framework) even went further in saying that he only provides the clone() method just "because people expect it". He does NOT actually recommend using it at all.


I think the more interesting debate is whether a copy constructor is better than a copy factory, but that's a different discussion altogether.

Community
  • 1
  • 1
polygenelubricants
  • 348,637
  • 121
  • 546
  • 611
  • 1
    Yep, this is one of my favorite parts of the book. – polygenelubricants Mar 01 '10 at 15:36
  • 1
    I don't like to say that clone() is broken. I prefer to say that clone was a terrible design decision and can harm you a lot if you don't use it properly. Also, you might never trust other people clone() methods. So we end up similar, try to avoid it, but it's not broken. – santiagobasulto Aug 30 '11 at 19:24
  • 4
    Doesn't using the copy ctor require you to know which implementation of Map you're copying? Seems like an unnecessary limitation. – jon-hanson Feb 15 '12 at 13:18
  • "that he only provides the clone() method just "because people expect it"" -- source? – Adam Parkin Mar 19 '13 at 18:32
60

Neither of the two: the constructor that you are referring to is defined for the HashMap implementation of a Map, (as well as for others) but not for the Map interface itself (for example, consider the Provider implementation of the Map interface: you will not find that constructor).

On the other hand it is not advisable to use the clone() method, as explained by Josh Bloch.

In respect of the Map interface (and of your question, in which you ask how to copy a Map, not a HashMap), you should use Map#putAll():

Copies all of the mappings from the specified map to this map (optional operation). The effect of this call is equivalent to that of calling put(k, v) on this map once for each mapping from key k to value v in the specified map.

Example:

// 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);
nont
  • 8,804
  • 6
  • 59
  • 78
Luca Fagioli
  • 10,385
  • 5
  • 46
  • 44
  • 2
    So then to clarify: if you *know* are copying *to* an implementation of `Map` which has a copy constructor there is no reason to not use the copy constructor then? – Adam Parkin May 22 '13 at 16:29
  • 2
    Exactly, and you can even think it the other way around: if you use `putAll` you _do not need to know_ if the `Map` implementation you are using has a copy constructor or not. A merely copy constructor of any `Map` implementation is thus redundant. – Luca Fagioli May 22 '13 at 16:39
  • 1
    Sure, though generally I like 1-liners better than 2-liners. ;) – Adam Parkin May 22 '13 at 16:56
11

Copy a map without knowing its implementation:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Terris
  • 567
  • 5
  • 12