Flache Kopie einer Karte in Java

106

Soweit ich weiß, gibt es verschiedene Möglichkeiten (möglicherweise auch andere), eine flache Kopie von a Mapin Java zu erstellen :

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

Wird ein Weg dem anderen vorgezogen, und wenn ja, warum?

Eine erwähnenswerte Sache ist, dass der zweite Weg eine "Unchecked Cast" -Warnung gibt. Sie müssen also etwas hinzufügen @SuppressWarnings("unchecked"), um es zu umgehen , was ein wenig irritierend ist (siehe unten).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}
dcp
quelle
In den neueren Versionen von Java (genauer gesagt seit Java 10) können Sie die statische Factory-Methode Map.copyOf verwenden. Beachten Sie jedoch, dass eine nicht veränderbare Karte zurückgegeben wird!
Oleksandr Pyrohov

Antworten:

106

Es ist immer besser, mit einem Kopierkonstruktor zu kopieren. clone()in Java ist kaputt (siehe SO: Wie überschreibe ich die Klonmethode richtig? ).

Josh Bloch über Design - Kopierkonstruktor versus Klonen

Wenn Sie den Artikel über das Klonen in meinem Buch gelesen haben, insbesondere wenn Sie zwischen den Zeilen lesen, werden Sie wissen, dass ich denke, dass er clonezutiefst gebrochen ist. [...] Es ist eine Schande, Cloneabledie kaputt ist, aber es passiert.

Bloch (der übrigens das Collection-Framework entworfen und implementiert hat) ging sogar noch weiter und sagte, dass er die clone()Methode nur "nur weil die Leute es erwarten" bereitstellt . Er empfiehlt es überhaupt nicht.


Ich denke, die interessantere Debatte ist, ob ein Kopierkonstruktor besser ist als eine Kopierfabrik, aber das ist eine ganz andere Diskussion.

Polygenschmierstoffe
quelle
1
Ja, das ist einer meiner Lieblingsteile des Buches.
Polygenelubricants
1
Ich möchte nicht sagen, dass clone () kaputt ist. Ich möchte lieber sagen, dass der Klon eine schreckliche Designentscheidung war und Ihnen sehr schaden kann, wenn Sie ihn nicht richtig verwenden. Außerdem können Sie anderen Clone () -Methoden möglicherweise niemals vertrauen. Also werden wir ähnlich, versuchen es zu vermeiden, aber es ist nicht kaputt.
Santiagobasulto
4
Wenn Sie den Kopiercode nicht verwenden, müssen Sie wissen, welche Implementierung von Map Sie kopieren? Scheint eine unnötige Einschränkung zu sein.
Jon-Hanson
"dass er nur die clone () -Methode bereitstellt, nur" weil die Leute es erwarten "" - Quelle?
Adam Parkin
60

Keiner der beiden: Der Konstruktor , auf den Sie sich beziehen, ist für die HashMap- Implementierung einer Map (sowie für andere) definiert, nicht jedoch für die Map-Schnittstelle selbst (betrachten Sie beispielsweise die Provider- Implementierung der Map-Schnittstelle: you wird diesen Konstruktor nicht finden).

Andererseits ist es nicht ratsam, die clone()von Josh Bloch erläuterte Methode zu verwenden .

In Bezug auf die Kartenschnittstelle (und Ihre Frage, in der Sie fragen, wie eine Karte kopiert werden soll, nicht eine HashMap) sollten Sie Map # putAll () verwenden :

Kopiert alle Zuordnungen von der angegebenen Karte in diese Karte (optionaler Vorgang). Der Effekt dieses Aufrufs entspricht dem des Aufrufs von put (k, v) auf dieser Map einmal für jede Zuordnung von Schlüssel k zu Wert v in der angegebenen Map.

Beispiel:

// 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);
Luca Fagioli
quelle
2
Um zu verdeutlichen: Wenn Sie wissen, dass Sie in eine Implementierung kopieren , für Mapdie ein Kopierkonstruktor vorhanden ist, gibt es keinen Grund, den Kopierkonstruktor dann nicht zu verwenden?
Adam Parkin
2
Genau, und Sie können es sogar anders herum denken: Wenn Sie verwenden, müssen putAllSie nicht wissen, ob die von MapIhnen verwendete Implementierung einen Kopierkonstruktor hat oder nicht. Ein bloßer Kopierkonstruktor einer MapImplementierung ist somit redundant.
Luca Fagioli
1
Klar, obwohl ich 1-Liner im Allgemeinen besser mag als 2-Liner. ;)
Adam Parkin
11

Kopieren Sie eine Karte, ohne deren Implementierung zu kennen:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Terris
quelle
3
Erwägen Sie das Hinzufügen von <K,V>Typparametern, um die Typensicherheit zu gewährleisten.
Barett
1
Was ist mit Karten ohne Null-Argument-Konstruktoren?
Isaac Saffold