3

Why can't I clone ConcurrentHashMap?

ConcurrentHashMap<String, String> test = new ConcurrentHashMap<String, String>();
    test.put("hello", "Salaam");

    ConcurrentHashMap<String, String> test2 = (ConcurrentHashMap<String, String> ) test.clone();

    System.out.println(test2.get("hello"));

If I use HashMap instead of ConcurrentHashMap, it works.

Mohammad Roohitavaf
  • 439
  • 2
  • 6
  • 15
  • Because, unlike [`HashMap`](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html), [`ConcurrentHashMap`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html) doesn't implement [`Cloneable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html). Highlighted by the fact, that your code **doesn't compile**, i.e. the method is not available!!! – Andreas Jul 17 '16 at 04:50
  • 1
    @Andreas: Despite the name, implementing `Cloneable` doesn't mean you support `clone`, and supporting `clone` doesn't require you to implement `Cloneable`. `Cloneable` doesn't actually have `clone` as a public method. It's one of the weird flaws of the `clone` design. – user2357112 Jul 17 '16 at 05:01
  • Supporting `clone` does require implementing the `Cloneable` interface, @user2357112. "Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown." - https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html – Lew Bloch Jul 17 '16 at 06:48
  • @LewBloch: Yeah, but if you don't use `Object::clone` in your `clone` implementation (which is sometimes reasonable), you don't need to implement `Cloneable`. It's not even like, say, `Comparable`, where if you implement `compareTo` without implementing `Comparable`, you lose out on all the library methods that take `Comparable` arguments. Nothing that uses `clone` takes `Cloneable`, because you can't `clone` a `Cloneable`. – user2357112 Jul 17 '16 at 06:56

2 Answers2

15

The clone() method on AbstractMap is not meant for copying, it is an internal method, note the protected keyword.

protected Object clone() throws CloneNotSupportedException {

HashMap happens to have a public clone(), but this does not mean you should use it, this is discussed further by Effective Java: Analysis of the clone() method

A more flexible way to create copies of collections is via the copy-constructors. These have advantage of creating any Map implementation from any other.

/**
 * Creates a new map with the same mappings as the given map.
 *
 * @param m the map
 */
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {

e.g.

ConcurrentHashMap<String, String> original = new ConcurrentHashMap<String, String>();
original.put("hello", "Salaam");

Map<String, String> copy = new ConcurrentHashMap<>(original);
original.remove("hello");
System.out.println(copy.get("hello"));
Community
  • 1
  • 1
Adam
  • 35,919
  • 9
  • 100
  • 137
  • 1
    Is the approach you suggest actually thread safe? Note that if the original map is a concurrent hash map. And you try to clone it via new HashMap(collection). The code will enter a loop of for (Map.Entry extends K, ? extends V> e : m.entrySet()) So if in the meantime someone goes to your concurrent hashmap and takeys a key value pairing their, you could have a problem while iterating over the elements. And the implementation of concurrent hash mapo contructor does ont look any safer... it just loops over the entry set of the original map. So bye bye explosion. – 99Sono Jun 23 '20 at 08:54
  • According to https://stackoverflow.com/questions/3768554/is-iterating-concurrenthashmap-values-thread-safe#:~:text=Iterators%20returned%20by%20ConcurrentHashMap.&text=However%2C%20iterators%20are%20designed%20to%20be%20used%20by%20only%20one,unexpected%20result%20in%20the%20application. It should actually be ok. – 99Sono Jun 23 '20 at 09:04
0

More elegant way is to perform deep cloning. If you do perform deep cloning, then there might exist a scenario in which you just copy the references stored in the concurrent hashmap and not the actual objects. That's why in such situations, deep cloning is preferred in which objects in the object graph are cloned. One of the easiest way of doing deep cloning is to make use of apache common lang library - SerializationUtils#clone.

<dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.6</version>
    </dependency>

You can use clone api of SerializationUtils class to perform deep cloning as follows -

ConcurrentHashMap<String, Vertex> transposeVertexMap = (ConcurrentHashMap<String, Vertex>) SerializationUtils.clone(vertexMap);
Nikhil Bhide
  • 728
  • 8
  • 23