Question

I find my self having to export references to keys in an underlying Java collections map. I don't want to give out references to the actual key objects but copies of them to ensure that nobody fiddles with the values and messes up the map. So I need to create a copy of an arbitrary generic type.

I already wrote a utility class that does this through in memory serialisation, but then noticed that Collections.nCopies also gives me copies of things.

So just to give some idea of what I am doing, I need to for instance access the next (higher/lower) key value given some other key value.

public class MapWrapper<T, K extends Comparable<K>>
    implements OtherThing<T, K>
{
    public K next (K current) {
        return ... cloned next highest key as per NavigableMap.higherKey()
    }
}

So the question is if it is better to copy the objects with

T copy = Collections.nCopies(1,item).get(0)

or to serialise the object and then deserialise it

final ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
final ObjectOutputStream outputStream = new ObjectOutputStream(outputBuffer);
outputStream.writeObject(item);
outputStream.close();

final ByteArrayInputStream inputBuffer = new ByteArrayInputStream(buffer);
final ObjectInputStream inputStream = new ObjectInputStream(inputBuffer);T copy = null;
T item = null;
try {
    copy = (T)inputStream.readObject();
} catch (ClassNotFoundException e) {
    Logger.getLogger(SerialisationService.class.getName()).log(Level.WARNING, null, e);
}

inputStream.close();
inputBuffer.close();
outputBuffer.close();

A quick test with copying a Long shows that the Collections.nCopies is faster (0 ms / 8 ms). Is there some caveat to that method that I'm not able to think about at this late hour though, or can you think of an even better way of getting a copy of the keys?

Was it helpful?

Solution

There is a significant difference - Collections.nCopies doesn't copy the object! It just returns a List containing n entries, all of which are the provided object (check the source!). Any changes made to anything you pick out from that list will be reflected in the original object. So this code:

Point original = new Point(0, 0);
Point supposedCopy = Collections.nCopies(1, original).get(0);
supposedCopy.x = 13;
System.out.println("x = " + original.x); 

will print 13, not 0. Oops.

A serialization roundtrip isn't a bad idea. An alternative is using Cloneable, which unfortunately is kind of broken. Cloneable doesn't declare clone(), but it is declared as protected in Object, so you have to invoke it by means of reflection.

Personally, I'd prefer to use immutable objects in your situation, if possible.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top