Bản sao nông của Bản đồ trong Java


106

Theo tôi hiểu, có một số cách (có thể có cả những cách khác) để tạo một bản sao cạn của một Maptrong 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();

Cách này có được ưu tiên hơn cách kia không, và nếu vậy, tại sao?

Một điều đáng nói là cách thứ hai đưa ra cảnh báo "Unchecked Cast". Vì vậy, bạn phải thêm @SuppressWarnings("unchecked")vào để có được xung quanh nó, hơi khó chịu (xem bên dưới).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}

Trong các phiên bản Java mới hơn (chính xác là kể từ Java 10), bạn có thể sử dụng phương thức nhà máy tĩnh Map.copyOf . Nhưng lưu ý rằng nó trả về một Bản đồ không thể sửa đổi!
Oleksandr Pyrohov

Câu trả lời:


106

Tốt hơn hết là sao chép bằng cách sử dụng một hàm tạo bản sao. clone()trong Java bị hỏng (xem SO: Làm thế nào để ghi đè đúng phương thức nhân bản? ).

Josh Bloch về thiết kế - Copy Constructor so với Cloning

Nếu bạn đã đọc mục về nhân bản trong cuốn sách của tôi, đặc biệt là nếu bạn đọc giữa các dòng, bạn sẽ biết rằng tôi nghĩ rằng clonenó đã bị phá vỡ sâu sắc. [...] Thật xấu hổ khi Cloneablebị phá vỡ, nhưng nó sẽ xảy ra.

Bloch (nhân tiện, thiết kế và triển khai khung Bộ sưu tập) thậm chí còn đi xa hơn khi nói rằng anh ta chỉ cung cấp clone()phương pháp chỉ "vì mọi người mong đợi nó". Anh ấy KHÔNG thực sự khuyên bạn nên sử dụng nó.


Tôi nghĩ rằng cuộc tranh luận thú vị hơn là liệu một nhà xây dựng bản sao có tốt hơn một nhà máy sao chép hay không, nhưng đó hoàn toàn là một cuộc thảo luận khác.


1
Đúng, đây là một trong những phần yêu thích của tôi trong cuốn sách.
polygenelubricants

1
Tôi không muốn nói rằng clone () bị hỏng. Tôi muốn nói rằng sao chép là một quyết định thiết kế tồi tệ và có thể gây hại cho bạn rất nhiều nếu bạn không sử dụng nó đúng cách. Ngoài ra, bạn có thể không bao giờ tin tưởng các phương thức clone () của người khác. Vì vậy, chúng ta kết thúc tương tự, cố gắng tránh nó, nhưng nó không bị hỏng.
santiagobasulto

4
Việc sử dụng ctor copy không yêu cầu bạn biết cách triển khai của Bản đồ mà bạn đang sao chép? Có vẻ như một hạn chế không cần thiết.
jon-hanson

"mà anh ta chỉ cung cấp phương thức clone () chỉ" bởi vì mọi người mong đợi nó "" - nguồn?
Adam Parkin

60

Cả hai đều không: phương thức khởi tạo mà bạn đang đề cập đến được xác định cho việc triển khai HashMap của một Bản đồ , (cũng như cho những người khác) nhưng không cho chính giao diện Bản đồ (ví dụ, hãy xem xét việc triển khai của Nhà cung cấp của giao diện Bản đồ: bạn sẽ không tìm thấy hàm tạo đó).

Mặt khác, không nên sử dụng clone()phương pháp này, như Josh Bloch giải thích.

Đối với giao diện Bản đồ (và câu hỏi của bạn, trong đó bạn hỏi cách sao chép Bản đồ, không phải HashMap), bạn nên sử dụng Map # putAll () :

Sao chép tất cả các ánh xạ từ bản đồ được chỉ định sang bản đồ này (thao tác tùy chọn). Hiệu quả của lệnh gọi này tương đương với việc gọi put (k, v) trên bản đồ này một lần cho mỗi ánh xạ từ khóa k đến giá trị v trong bản đồ được chỉ định.

Thí dụ:

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

2
Vì vậy, sau đó để làm rõ: nếu bạn biết đang sao chép vào một triển khai trong Mapđó có một hàm tạo bản sao thì không có lý do gì để không sử dụng hàm tạo bản sao?
Adam Parkin

2
Chính xác, và bạn thậm chí có thể nghĩ theo cách khác: nếu bạn sử dụng, putAllbạn không cần biết việc Maptriển khai bạn đang sử dụng có một hàm tạo bản sao hay không. Do đó, một hàm tạo sao chép đơn thuần của bất kỳ Maptriển khai nào là dư thừa.
Luca Fagioli

1
Chắc chắn, mặc dù nói chung tôi thích 1 lớp lót hơn 2 lớp lót. ;)
Adam Parkin

11

Sao chép bản đồ mà không biết cách triển khai:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}

3
Xem xét thêm <K,V>các tham số kiểu để giúp đảm bảo an toàn cho kiểu.
Barett

1
Điều gì về bản đồ không có hàm tạo đối số?
Isaac Saffold
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.