Salinan dangkal Peta di Jawa

107

Seperti yang saya pahami, ada beberapa cara (mungkin yang lain juga) untuk membuat salinan dangkal a Mapdi 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();

Apakah satu cara lebih disukai daripada yang lain, dan jika ya, mengapa?

Satu hal yang perlu disebutkan adalah bahwa cara kedua memberikan peringatan "Pemeran Tidak Dicentang". Jadi Anda harus menambahkan @SuppressWarnings("unchecked")untuk menyiasatinya, yang sedikit menjengkelkan (lihat di bawah).

@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
sumber
Di versi Java yang lebih baru (tepatnya sejak Java 10), Anda dapat menggunakan metode pabrik statis Map.copyOf . Tetapi perhatikan bahwa itu mengembalikan Peta yang tidak dapat dimodifikasi!
Oleksandr Pyrohov

Jawaban:

106

Itu selalu lebih baik untuk menyalin menggunakan konstruktor salinan. clone()di Java rusak (lihat SO: Bagaimana cara mengganti metode klon dengan benar? ).

Josh Bloch tentang Desain - Salin Pembuat versus Kloning

Jika Anda telah membaca item tentang kloning di buku saya, terutama jika Anda membaca yang tersirat, Anda akan tahu bahwa menurut saya clonesangat rusak. [...] Sayang sekali yang Cloneablerusak, tapi itu terjadi.

Bloch (yang merancang dan mengimplementasikan framework Collection) bahkan melangkah lebih jauh dengan mengatakan bahwa dia hanya menyediakan clone()metode hanya "karena orang-orang mengharapkannya". Dia TIDAK benar-benar merekomendasikan untuk menggunakannya sama sekali.


Saya pikir perdebatan yang lebih menarik adalah apakah pembuat salinan lebih baik daripada pabrik salinan, tetapi itu adalah diskusi yang berbeda sama sekali.

poligenelubricants
sumber
1
Ya, ini adalah salah satu bagian favorit saya dari buku ini.
poligenelubricants
1
Saya tidak suka mengatakan bahwa klon () rusak. Saya lebih suka mengatakan bahwa klon adalah keputusan desain yang buruk dan dapat sangat merugikan Anda jika Anda tidak menggunakannya dengan benar. Selain itu, Anda mungkin tidak pernah mempercayai metode clone () orang lain. Jadi kami akhirnya serupa, mencoba menghindarinya, tetapi tidak rusak.
santiagobasulto
4
Tidakkah menggunakan copy ctor mengharuskan Anda mengetahui implementasi Map yang Anda salin? Sepertinya batasan yang tidak perlu.
jon-hanson
"bahwa dia hanya menyediakan metode clone () hanya" karena orang mengharapkannya "" - sumber?
Adam Parkin
60

Tak satu pun dari keduanya: konstruktor yang Anda maksud ditentukan untuk implementasi HashMap dari sebuah Peta , (serta untuk yang lain) tetapi tidak untuk antarmuka Peta itu sendiri (misalnya, pertimbangkan implementasi Penyedia dari antarmuka Peta: Anda tidak akan menemukan konstruktor itu).

Di sisi lain, tidak disarankan menggunakan clone()metode tersebut, seperti yang dijelaskan oleh Josh Bloch.

Sehubungan dengan antarmuka Peta (dan pertanyaan Anda, di mana Anda menanyakan cara menyalin Peta, bukan HashMap), Anda harus menggunakan Map # putAll () :

Salin semua pemetaan dari peta yang ditentukan ke peta ini (operasi opsional). Efek dari panggilan ini setara dengan panggilan put (k, v) pada peta ini satu kali untuk setiap pemetaan dari kunci k ke nilai v dalam peta yang ditentukan.

Contoh:

// 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
sumber
2
Jadi untuk mengklarifikasi: jika Anda tahu menyalin ke implementasi Mapyang memiliki konstruktor salinan, tidak ada alasan untuk tidak menggunakan konstruktor salinan itu?
Adam Parkin
2
Tepatnya, dan Anda bahkan dapat berpikir sebaliknya: jika Anda menggunakan, putAllAnda tidak perlu mengetahui apakah Mapimplementasi yang Anda gunakan memiliki konstruktor salinan atau tidak. Dengan demikian, hanya menyalin konstruktor dari Mapimplementasi apa pun yang berlebihan.
Luca Fagioli
1
Tentu, meskipun secara umum saya lebih suka 1-liners daripada 2-liners. ;)
Adam Parkin
11

Salin peta tanpa mengetahui implementasinya:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Terris
sumber
3
Pertimbangkan untuk menambahkan <K,V>parameter tipe untuk membantu memastikan keamanan tipe.
Barett
1
Bagaimana dengan peta tanpa konstruktor tanpa argumen?
Isaac Saffold