Bộ sưu tập bất biến của Java


115

Từ tài liệu Khung Bộ sưu tập Java 1.6 :

Bộ sưu tập không hỗ trợ bất kỳ hoạt động sửa đổi (ví dụ như add, removeclear) được gọi là unmodifiable . [...] Các bộ sưu tập cũng đảm bảo rằng không có thay đổi nào trong đối tượng Bộ sưu tập sẽ được hiển thị được gọi là bất biến .

Tiêu chí thứ hai làm tôi bối rối một chút. Với bộ sưu tập đầu tiên là không thể sửa đổi và giả sử rằng tham chiếu bộ sưu tập ban đầu đã bị loại bỏ, những thay đổi được đề cập đến trong dòng thứ hai là gì? Nó có đề cập đến những thay đổi trong các phần tử được tổ chức trong bộ sưu tập tức là trạng thái của các phần tử không?

Câu hỏi thứ hai:
Để một bộ sưu tập là bất biến, làm thế nào để cung cấp các guarentees bổ sung được chỉ định? Nếu trạng thái của một phần tử trong tập hợp được cập nhật bởi một tiểu trình, thì liệu có đủ cho tính bất biến mà những cập nhật trong trạng thái đó không hiển thị trên tiểu trình giữ tập hợp bất biến không?

Để một tập hợp là bất biến, làm thế nào để cung cấp các bảo đảm bổ sung được chỉ định?


Nói chung (đặc biệt là trong các ngôn ngữ chức năng), bộ sưu tập bất biến (hay còn gọi là liên tục) có thể thay đổi theo nghĩa là bạn có thể nhận được trạng thái mới của bộ sưu tập này, nhưng đồng thời trạng thái sẽ vẫn có sẵn từ các liên kết khác. Ví dụ: newCol = oldCol.add("element")sẽ tạo ra bộ sưu tập mới là bản sao của bộ sưu tập cũ có thêm 1 phần tử và tất cả các tham chiếu đến oldColsẽ vẫn trỏ đến cùng một bộ sưu tập cũ không thay đổi.
ffriend

Câu trả lời:


154

Các bộ sưu tập không thể sửa đổi thường là các chế độ xem chỉ đọc (trình bao bọc) của các bộ sưu tập khác. Bạn không thể thêm, xóa hoặc xóa chúng, nhưng bộ sưu tập cơ bản có thể thay đổi.

Các tập hợp bất biến hoàn toàn không thể thay đổi - chúng không bao bọc một tập hợp khác - chúng có các phần tử riêng.

Đây là trích dẫn từ ổi của ImmutableList

Không giống như Collections.unmodifiableList(java.util.List<? extends T>), là một chế độ xem của một tập hợp riêng biệt vẫn có thể thay đổi, một thể hiện của ImmutableListchứa dữ liệu riêng tư của chính nó và sẽ không bao giờ thay đổi.

Vì vậy, về cơ bản, để có được một tập hợp bất biến từ một tập hợp có thể thay đổi, bạn phải sao chép các phần tử của nó vào tập hợp mới và không cho phép tất cả các thao tác.


@Bhaskar - xem đoạn cuối cùng của tôi.
Bozho

1
nếu bộ sưu tập được bao bọc bên trong một bộ sưu tập không thể thay đổi khác không có bất kỳ tham chiếu nào khác đến nó, thì bahaviour của bộ sưu tập không thể thay đổi có giống hệt với một bộ sưu tập bất biến không?
Bhaskar

1
@Bozho, Nếu tham chiếu đến bộ sưu tập sao lưu không được cung cấp và chỉ cung cấp tham chiếu trình bao bọc bộ sưu tập không thể thay đổi được thì không có cách nào bạn có thể thay đổi nó. Vậy tại sao bạn lại nói "Bạn không thể chắc chắn"? Nó có chỉ ra một tình huống trong đó một số luồng hoặc bằng cách nào đó nó bị sửa đổi vì bộ sưu tập sao lưu có thể sửa đổi được không?
AKS

@AKS: Khi một bộ sưu tập được bao bọc trong unmodifiableList, mã chỉ nhận tham chiếu đến danh sách đó sẽ không thể sửa đổi nó, nhưng bất kỳ mã nào có tham chiếu đến danh sách gốc và có thể sửa đổi nó trước khi trình bao bọc được tạo sẽ vẫn có thể làm như vậy sau đó. Nếu mã tạo ra danh sách ban đầu biết điều gì đã xảy ra với mọi tham chiếu đã từng tồn tại với nó và biết rằng không ai trong số chúng sẽ rơi vào tay mã có thể sửa đổi danh sách, thì nó có thể biết danh sách sẽ không bao giờ đã sửa đổi. Nếu tham chiếu đã nhận được từ mã bên ngoài, tuy nhiên ...
supercat

... không có cách nào để unmodifiableList, cũng như bất kỳ mã nào sử dụng nó, biết liệu bộ sưu tập được bọc có thể thay đổi hay không.
supercat

86

Sự khác biệt là bạn không thể có tham chiếu đến một tập hợp bất biến cho phép thay đổi. Bộ sưu tập không thể thay đổi không thể sửa đổi thông qua tham chiếu đó , nhưng một số đối tượng khác có thể trỏ đến cùng một dữ liệu mà qua đó nó có thể được thay đổi.

ví dụ

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);

1
Những nguyên nhân khác nhau, nếu có, có thể dẫn đến sự thay đổi trong tập hợp không thể sửa đổi, miễn là tham chiếu tập hợp ban đầu không được sử dụng để sửa đổi tập hợp cơ bản?
Bhaskar

21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1có thể thay đổi (nghĩa là không thể thay đổi hoặc không thể thay đổi ).
c2unmodifiable : nó không thể thay đổi bản thân, nhưng nếu sau này tôi thay đổi c1sau đó rằng sự thay đổi sẽ được hiển thị trong c2.

Điều này là do c2đơn giản là một trình bao bọc xung quanh c1và không thực sự là một bản sao độc lập. Guava cung cấp ImmutableListgiao diện và một số triển khai. Chúng hoạt động bằng cách thực sự tạo một bản sao của đầu vào (trừ khi đầu vào là một tập hợp bất biến của riêng nó).

Về câu hỏi thứ hai của bạn:

Khả năng thay đổi / bất biến của một tập hợp không phụ thuộc vào khả năng thay đổi / bất biến của các đối tượng chứa trong đó. Việc sửa đổi một đối tượng có trong một bộ sưu tập không được tính là "sửa đổi bộ sưu tập" cho mô tả này. Tất nhiên nếu bạn cần một bộ sưu tập bất biến, bạn cũng thường muốn nó chứa các đối tượng bất biến.


2
@John: không, không phải đâu. Ít nhất là không theo định nghĩa được trích dẫn bởi OP.
Joachim Sauer

@JohnVint, thật không? Tôi sẽ không nghĩ như vậy, bởi vì sử dụng c1, tôi vẫn có thể thêm các phần tử vào đó và phần bổ sung này sẽ hiển thị với c2. Điều đó không có nghĩa là nó không phải là bất biến?
Bhaskar

Tôi đoán nếu c1 có thể thoát thì nó sẽ không phải là bất biến, nhưng tính bất biến cũng đề cập đến nội dung trong bộ sưu tập. Tôi đã được nhiều đề cập đến một unmodifiableCollection của java.util.Date của
John Vint

1
@ Gợi ý: "if c1not Escape" không phải là sự cân nhắc được phân biệt ở bất kỳ đâu trong thông số kỹ thuật. Theo tôi, nếu bạn có thể sử dụng bộ sưu tập theo cách làm cho nó không thể thay đổi, thì bạn hãy luôn coi nó là không thể thay đổi.
Joachim Sauer

1
Hãy để tôi trích dẫn định nghĩa: "Các bộ sưu tập đảm bảo bổ sung ..." (nhấn mạnh của tôi). Collection.unmodifiableList() không thể đảm bảo điều đó, bởi vì nó không thể đảm bảo rằng đối số của nó không thoát ra. Ổi ImmutableList.of luôn tạo ra sự bất biến List, ngay cả khi bạn để lý lẽ của nó thoát ra.
Joachim Sauer

17

Bây giờ java 9 có các phương thức ban đầu cho Danh sách bất biến, Tập hợp, Bản đồ và Bản đồ.Entry.

Trong Java SE 8 và các phiên bản trước đó, Chúng ta có thể sử dụng các phương thức tiện ích của lớp Collections như unmodifiableXXX để tạo các đối tượng Immutable Collection.

Tuy nhiên, các phương thức Collections.unmodifiableXXX này rất tẻ nhạt và cách tiếp cận dài dòng. Để khắc phục những thiếu sót đó, Oracle corp đã thêm một số phương thức tiện ích vào các giao diện List, Set và Map.

Bây giờ trong java 9: ​​- Các giao diện List và Set có các phương thức “of ()” để tạo một danh sách bất biến hoặc các đối tượng Set trống hoặc không trống như hình bên dưới:

Ví dụ về danh sách trống

List immutableList = List.of();

Ví dụ về danh sách không trống

List immutableList = List.of("one","two","three");

2
Và trong Java 10 List.copyOfSet.copyOfđã được thêm vào cho phép tạo ra một bản sao unmodifiable của một danh sách / bộ, hoặc trả lại bộ sưu tập được nếu nó đã unmodifiable, xem JDK-8.191.517
Marcono1234

6

Tôi tin rằng điểm mấu chốt ở đây là ngay cả khi một bộ sưu tập là Không thể sửa đổi, điều đó không đảm bảo rằng nó không thể thay đổi. Lấy ví dụ một bộ sưu tập loại bỏ các phần tử nếu chúng quá cũ. Không thể thay đổi chỉ có nghĩa là đối tượng giữ tham chiếu không thể thay đổi nó, không phải là nó không thể thay đổi. Một ví dụ thực sự của điều này là Collections.unmodifiableListphương pháp. Nó trả về một chế độ xem không thể thay đổi của một Danh sách. Tham chiếu Danh sách đã được chuyển vào phương thức này vẫn có thể sửa đổi được và vì vậy danh sách có thể được sửa đổi bởi bất kỳ chủ sở hữu tham chiếu nào đã được chuyển qua. Điều này có thể dẫn đến ConcurrentModificationExceptions và những điều xấu khác.

Bất biến, nghĩa là không có cách nào có thể thay đổi tập hợp.

Câu hỏi thứ hai: Một tập hợp bất biến không có nghĩa là các đối tượng có trong tập hợp sẽ không thay đổi, chỉ là tập hợp đó sẽ không thay đổi về số lượng và thành phần của các đối tượng mà nó chứa. Nói cách khác, danh sách tài liệu tham khảo của bộ sưu tập sẽ không thay đổi. Điều đó không có nghĩa là bên trong của đối tượng được tham chiếu không thể thay đổi.


Tốt !! Vì vậy, nếu tôi tạo một trình bao bọc trên Bộ sưu tập không thể thay đổi và đặt tham chiếu bộ sưu tập có thể sửa đổi là riêng tư thì tôi có thể chắc chắn rằng nó là bất biến. đúng?
AKS

Nếu tôi hiểu câu hỏi của bạn, thì câu trả lời là không. Việc bọc Unmodifiable không có tác dụng gì để bảo vệ nó khỏi việc chuyển bộ sưu tập để unmodifiableListsửa đổi. Nếu bạn muốn sử dụng điều này ImmutableList.
John B

0

Pure4J hỗ trợ những gì bạn đang theo đuổi, theo hai cách.

Đầu tiên, nó cung cấp một @ImmutableValuechú thích, để bạn có thể chú thích một lớp để nói rằng nó là bất biến. Có một plugin maven để cho phép bạn kiểm tra xem mã của bạn có thực sự là bất biến (sử dụng, finalv.v.).

Thứ hai, nó cung cấp các bộ sưu tập liên tục từ Clojure, (với các generic được bổ sung) và đảm bảo rằng các phần tử được thêm vào các bộ sưu tập là bất biến. Hiệu suất của những thứ này rõ ràng là khá tốt. Tất cả các bộ sưu tập là bất biến, nhưng triển khai các giao diện bộ sưu tập java (và các giao diện chung) để kiểm tra. Mutation trả về bộ sưu tập mới.

Tuyên bố từ chối trách nhiệm: Tôi là người phát triển điều này

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.