Trước đây, tôi từng nói sẽ sao chép một cách an toàn một bộ sưu tập làm một việc như:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
hoặc là
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Nhưng các hàm tạo "sao chép" này, các phương thức và luồng tạo tĩnh tương tự, có thực sự an toàn không và các quy tắc được chỉ định ở đâu? Nói một cách an toàn, ý tôi là các bảo đảm toàn vẹn ngữ nghĩa cơ bản được cung cấp bởi ngôn ngữ Java và các bộ sưu tập được thi hành chống lại một người gọi độc hại, giả sử được hỗ trợ bởi một hợp lý SecurityManager
và không có sai sót.
Tôi hài lòng với phương pháp ném ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, vv, hoặc thậm chí treo.
Tôi đã chọn String
làm một ví dụ về một đối số loại bất biến. Đối với câu hỏi này, tôi không quan tâm đến các bản sao sâu cho các bộ sưu tập các loại có thể thay đổi có các vấn đề riêng.
(Để rõ ràng, tôi đã xem mã nguồn OpenJDK và có một số loại câu trả lời cho ArrayList
và TreeSet
.)
NavigableSet
và các Comparable
bộ sưu tập dựa trên khác đôi khi có thể phát hiện nếu một lớp không thực hiện compareTo()
đúng và đưa ra một ngoại lệ. Có một chút không rõ ý của bạn là gì bởi những lý lẽ không đáng tin cậy. Bạn có nghĩa là một kẻ bất lương thủ công một bộ sưu tập các chuỗi xấu và khi bạn sao chép chúng vào bộ sưu tập của bạn thì điều gì đó xấu xảy ra? Không, khung bộ sưu tập khá chắc chắn, nó đã có từ ngày 1.2.
HashSet
(và tất cả các bộ sưu tập băm khác nói chung) phụ thuộc vào tính chính xác / tính toàn vẹn của việc hashCode
triển khai các phần tử TreeSet
và PriorityQueue
phụ thuộc vào Comparator
(và thậm chí bạn không thể tạo một bản sao tương đương mà không chấp nhận bộ so sánh tùy chỉnh nếu có), EnumSet
tin tưởng vào tính toàn vẹn của enum
loại cụ thể không bao giờ được xác minh sau khi biên dịch, do đó, một tệp lớp, không được tạo javac
hoặc làm thủ công, có thể lật đổ nó.
new TreeSet<>(strs)
nơi strs
là a NavigableSet
. Đây không phải là một bản sao số lượng lớn, vì kết quả TreeSet
sẽ sử dụng bộ so sánh của nguồn, thậm chí còn cần thiết để giữ lại ngữ nghĩa. Nếu bạn ổn chỉ với việc xử lý các yếu tố có trong đó, toArray()
là cách để đi; nó thậm chí sẽ giữ thứ tự lặp. Khi bạn ổn với phần tử lấy, xác thực phần tử, sử dụng phần tử, bạn thậm chí không cần tạo một bản sao. Các vấn đề bắt đầu khi bạn muốn xác minh tất cả các yếu tố, tiếp theo là sử dụng tất cả các yếu tố. Sau đó, bạn không thể tin tưởng vào một bộ TreeSet
so sánh tùy chỉnh sao chép
checkcast
mỗi phần tử, toArray
với một loại cụ thể. Chúng tôi luôn luôn kết thúc ở đó. Các bộ sưu tập chung thậm chí không biết loại phần tử thực tế của chúng, vì vậy các hàm tạo sao chép của chúng không thể cung cấp chức năng tương tự. Tất nhiên, bạn có thể trì hoãn bất kỳ kiểm tra nào để sử dụng đúng trước đó, nhưng sau đó, tôi không biết câu hỏi của bạn đang nhắm đến là gì. Bạn không cần "tính toàn vẹn ngữ nghĩa", khi bạn ổn với việc kiểm tra và thất bại ngay lập tức trước khi sử dụng các yếu tố.