Tại sao Java 8 không bao gồm các bộ sưu tập bất biến?


130

Nhóm Java đã thực hiện rất nhiều công việc tuyệt vời để loại bỏ các rào cản đối với lập trình chức năng trong Java 8. Đặc biệt, các thay đổi đối với Bộ sưu tập java.util thực hiện rất tốt việc chuyển đổi các chuỗi thành các hoạt động truyền phát rất nhanh. Xem xét công việc họ đã thực hiện tốt như thế nào khi thêm các hàm hạng nhất và phương thức chức năng vào các bộ sưu tập, tại sao họ hoàn toàn thất bại trong việc cung cấp các bộ sưu tập bất biến hoặc thậm chí các giao diện bộ sưu tập bất biến?

Không thay đổi bất kỳ mã hiện có nào, nhóm Java bất cứ lúc nào cũng có thể thêm các giao diện bất biến giống như các giao diện có thể thay đổi, trừ các phương thức "thiết lập" và làm cho các giao diện hiện có mở rộng từ chúng, như sau:

                  ImmutableIterable
     ____________/       |
    /                    |
Iterable        ImmutableCollection
   |    _______/    /          \   \___________
   |   /           /            \              \
 Collection  ImmutableList  ImmutableSet  ImmutableMap  ...
    \  \  \_________|______________|__________   |
     \  \___________|____________  |          \  |
      \___________  |            \ |           \ |
                  List            Set           Map ...

Chắc chắn, các hoạt động như List.add () và Map.put () hiện trả về giá trị boolean hoặc trước đó cho khóa đã cho để cho biết thao tác thành công hay thất bại. Các bộ sưu tập bất biến sẽ phải xử lý các phương thức như các nhà máy và trả lại một bộ sưu tập mới có chứa phần tử được thêm vào - không tương thích với chữ ký hiện tại. Nhưng điều đó có thể được giải quyết bằng cách sử dụng một tên phương thức khác như ImmutableList.append () hoặc .addAt () và ImmutableMap.putEntry (). Độ dài kết quả sẽ lớn hơn nhiều so với lợi ích của việc làm việc với các bộ sưu tập không thay đổi và hệ thống loại sẽ ngăn các lỗi gọi phương thức sai. Theo thời gian, các phương pháp cũ có thể bị phản đối.

Thắng các bộ sưu tập bất biến:

  • Đơn giản - lý luận về mã đơn giản hơn khi dữ liệu cơ bản không thay đổi.
  • Tài liệu - nếu một phương thức có giao diện bộ sưu tập bất biến, bạn biết rằng nó sẽ không sửa đổi bộ sưu tập đó. Nếu một phương thức trả về một bộ sưu tập bất biến, bạn biết bạn không thể sửa đổi nó.
  • Đồng thời - bộ sưu tập bất biến có thể được chia sẻ an toàn trên các chủ đề.

Là một người đã nếm trải các ngôn ngữ giả định sự bất biến, rất khó để quay trở lại miền Tây hoang dã của sự đột biến tràn lan. Các bộ sưu tập của Clojure (trừu tượng hóa chuỗi) đã có mọi thứ mà các bộ sưu tập Java 8 cung cấp, cộng với tính không thay đổi (mặc dù có thể sử dụng thêm bộ nhớ và thời gian do danh sách liên kết được đồng bộ hóa thay vì các luồng). Scala có cả bộ sưu tập có thể thay đổi và bất biến với một bộ đầy đủ các thao tác và mặc dù các thao tác đó rất háo hức, việc gọi .iterator đưa ra một cái nhìn lười biếng (và có nhiều cách khác để đánh giá chúng một cách lười biếng). Tôi không thấy Java có thể tiếp tục cạnh tranh như thế nào nếu không có các bộ sưu tập bất biến.

Ai đó có thể chỉ cho tôi lịch sử hoặc thảo luận về điều này? Chắc chắn nó ở nơi nào đó.


9
Liên quan đến điều này - Ayende đã viết blog gần đây về các bộ sưu tập và bộ sưu tập bất biến trong C #, với điểm chuẩn. ayende.com/blog/tags/performance - tl; dr - tính bất biến là chậm .
Oded 18/12/13

20
với hệ thống phân cấp của bạn, tôi có thể cung cấp cho bạn một Danh sách bất biến và sau đó thay đổi nó khi bạn không mong đợi nó có thể phá vỡ nhiều thứ, vì bạn chỉ có các constbộ sưu tập
ratchet freak

18
@Oded Bất biến là chậm, nhưng khóa cũng vậy. Vậy là duy trì một lịch sử. Đơn giản / Chính xác là giá trị tốc độ trong nhiều tình huống. Với các bộ sưu tập nhỏ, tốc độ không phải là vấn đề. Phân tích của Ayende dựa trên giả định rằng bạn không cần lịch sử, khóa hoặc đơn giản và rằng bạn đang làm việc với một tập dữ liệu lớn. Đôi khi điều đó là đúng, nhưng đó không phải là điều luôn luôn tốt hơn. Có sự đánh đổi.
GlenPeterson

5
@GlenPeterson đó là những gì bản sao phòng thủ và Collections.unmodifiable*()dành cho. nhưng đừng coi những điều này là bất biến khi chúng không
ratchet freak

13
Hở? Nếu chức năng của bạn có một ImmutableListtrong sơ đồ đó, mọi người có thể vượt qua trong một đột biến List? Không, đó là một sự vi phạm LSP rất tệ.
Telastyn

Câu trả lời:


113

Bởi vì bộ sưu tập bất biến hoàn toàn yêu cầu chia sẻ để có thể sử dụng. Mặt khác, mọi hoạt động đơn lẻ sẽ thả cả một danh sách khác vào đống ở đâu đó. Các ngôn ngữ hoàn toàn bất biến, như Haskell, tạo ra lượng rác đáng kinh ngạc mà không cần tối ưu hóa và chia sẻ mạnh mẽ. Có bộ sưu tập chỉ sử dụng được với <50 yếu tố không đáng để đưa vào thư viện chuẩn.

Hơn nữa, các bộ sưu tập bất biến thường có những triển khai khác nhau về cơ bản so với các đối tác có thể thay đổi của chúng. Ví dụ ArrayList, xem xét một bất biến hiệu quả ArrayListsẽ không phải là một mảng! Nó nên được thực hiện với một cây cân bằng với hệ số phân nhánh lớn, Clojure sử dụng 32 IIRC. Làm cho các bộ sưu tập có thể thay đổi là "bất biến" bằng cách thêm một bản cập nhật chức năng là một lỗi hiệu năng cũng giống như rò rỉ bộ nhớ.

Hơn nữa, việc chia sẻ không khả thi trong Java. Java cung cấp quá nhiều móc nối không giới hạn đối với tính biến đổi và tính công bằng tham chiếu để chia sẻ "chỉ là một tối ưu hóa". Có thể bạn sẽ khó chịu một chút nếu bạn có thể sửa đổi một yếu tố trong danh sách và nhận ra bạn vừa sửa đổi một yếu tố trong 20 phiên bản khác của danh sách đó bạn có.

Điều này cũng loại trừ các lớp lớn tối ưu hóa rất quan trọng cho tính bất biến hiệu quả, chia sẻ, hợp nhất luồng, bạn đặt tên cho nó, khả năng biến đổi phá vỡ nó. (Điều đó sẽ tạo ra một khẩu hiệu tốt cho các nhà truyền giáo FP)


21
Ví dụ của tôi đã nói về giao diện bất biến . Java có thể cung cấp một bộ đầy đủ cả hai triển khai có thể thay đổi và bất biến của các giao diện đó sẽ tạo ra sự đánh đổi cần thiết. Tùy thuộc vào lập trình viên để chọn đột biến hoặc bất biến khi thích hợp. Các lập trình viên phải biết khi nào nên sử dụng Danh sách so với Đặt ngay. Bạn thường không cần phiên bản có thể thay đổi cho đến khi bạn gặp vấn đề về hiệu năng, và sau đó có thể chỉ cần thiết là người xây dựng. Trong mọi trường hợp, có giao diện bất biến sẽ là một chiến thắng của riêng nó.
GlenPeterson

4
Tôi đọc lại câu trả lời của bạn và tôi nghĩ rằng bạn đang nói rằng Java có một giả định cơ bản về khả năng biến đổi (ví dụ như đậu java) và các bộ sưu tập chỉ là phần nổi của tảng băng và khắc phục mẹo đó sẽ không giải quyết được vấn đề tiềm ẩn. Một điểm hợp lệ. Tôi có thể chấp nhận câu trả lời này và tăng tốc việc áp dụng Scala! :-)
GlenPeterson

8
Tôi không chắc các bộ sưu tập bất biến đòi hỏi khả năng chia sẻ các phần chung là hữu ích. Loại bất biến phổ biến nhất trong Java, một tập hợp các ký tự bất biến, được sử dụng để cho phép chia sẻ nhưng không còn nữa. Điều quan trọng làm cho nó hữu ích là khả năng sao chép nhanh chóng dữ liệu từ a Stringvào StringBuffer, thao tác và sau đó sao chép dữ liệu vào một bất biến mới String. Sử dụng một mẫu như vậy với các bộ và danh sách có thể tốt như sử dụng các loại không thay đổi được thiết kế để tạo thuận lợi cho việc sản xuất các trường hợp thay đổi một chút, nhưng vẫn có thể tốt hơn ...
supercat

3
Hoàn toàn có thể thực hiện một bộ sưu tập bất biến trong Java bằng cách sử dụng chia sẻ. Các mục được lưu trữ trong bộ sưu tập là các tài liệu tham khảo và các tham chiếu của chúng có thể bị thay đổi - vậy thì sao? Hành vi như vậy đã phá vỡ các bộ sưu tập hiện có như HashMap và Treeset, nhưng các bộ sưu tập này được triển khai trong Java. Và nếu nhiều bộ sưu tập chứa các tham chiếu đến cùng một đối tượng, thì việc sửa đổi đối tượng sẽ gây ra thay đổi hiển thị khi nó được xem từ tất cả các bộ sưu tập.
Bí mật của Solomonoff

4
jozefg, hoàn toàn có thể thực hiện các bộ sưu tập bất biến hiệu quả trên JVM với chia sẻ cấu trúc. Scala và Clojure có chúng như một phần của thư viện tiêu chuẩn của họ, cả hai triển khai đều dựa trên HAMT của Phil Bagwell (Hash Array Mapped Trie). Tuyên bố của bạn về Clojure thực hiện các cấu trúc dữ liệu bất biến với các cây BALANCED là hoàn toàn sai.
vừng

78

Một bộ sưu tập có thể thay đổi không phải là một kiểu con của một bộ sưu tập bất biến. Thay vào đó, các bộ sưu tập có thể thay đổi và bất biến là anh em ruột của các bộ sưu tập có thể đọc được. Thật không may, các khái niệm "chỉ đọc", "chỉ đọc" và "không thay đổi" dường như bị làm mờ cùng nhau, mặc dù chúng có nghĩa là ba điều khác nhau.

  • Một lớp cơ sở bộ sưu tập hoặc loại giao diện có thể đọc được hứa hẹn rằng người ta có thể đọc các mục và không cung cấp bất kỳ phương tiện trực tiếp nào để sửa đổi bộ sưu tập, nhưng không đảm bảo rằng mã nhận được tham chiếu không thể truyền hoặc thao tác theo cách cho phép sửa đổi.

  • Giao diện bộ sưu tập chỉ đọc không bao gồm bất kỳ thành viên mới nào, nhưng chỉ nên được triển khai bởi một lớp hứa hẹn rằng không có cách nào để thao tác tham chiếu đến nó theo cách làm thay đổi bộ sưu tập cũng như không nhận được tham chiếu đến thứ gì đó điều đó có thể làm như vậy Tuy nhiên, điều đó không hứa hẹn rằng bộ sưu tập sẽ không được sửa đổi bởi một thứ khác có liên quan đến nội bộ. Lưu ý rằng giao diện bộ sưu tập chỉ đọc có thể không thể ngăn chặn việc thực hiện bởi các lớp có thể thay đổi, nhưng có thể chỉ định rằng bất kỳ triển khai hoặc lớp nào có nguồn gốc từ một triển khai, cho phép đột biến sẽ được coi là triển khai "bất hợp pháp" hoặc phái sinh của việc triển khai .

  • Một bộ sưu tập bất biến là một bộ sưu tập sẽ luôn giữ cùng một dữ liệu miễn là có bất kỳ tham chiếu nào đến nó. Bất kỳ việc thực hiện giao diện bất biến nào không phải lúc nào cũng trả lại cùng một dữ liệu để đáp ứng với một yêu cầu cụ thể đã bị hỏng.

Đó là đôi khi hữu ích để có loại mạnh mẽ liên quan có thể thay đổi và không thể thay đổi bộ sưu tập mà cả hai thực hiện hoặc xuất phát từ cùng loại "dễ đọc", và có loại có thể đọc được bao gồm AsImmutable, AsMutableAsNewMutablephương pháp. Thiết kế như vậy có thể cho phép mã muốn duy trì dữ liệu trong bộ sưu tập để gọi AsImmutable; phương pháp đó sẽ tạo một bản sao phòng thủ nếu bộ sưu tập có thể thay đổi, nhưng bỏ qua bản sao nếu nó không thay đổi.


1
Câu trả lời chính xác. Các bộ sưu tập không thay đổi có thể cung cấp cho bạn một sự đảm bảo khá mạnh mẽ liên quan đến độ an toàn của luồng và cách bạn có thể suy luận về chúng khi thời gian trôi qua. Một bộ sưu tập chỉ đọc / đọc được thì không. Trên thực tế, để tôn trọng nguyên tắc của trạm biến áp liskov, Chỉ đọc và Không thay đổi có lẽ phải là loại cơ sở trừu tượng với phương thức cuối cùng và các thành viên tư nhân để đảm bảo rằng không có lớp dẫn xuất nào có thể phá hủy được bảo vệ theo loại. Hoặc chúng phải là loại cụ thể hoàn toàn bao bọc một bộ sưu tập (Chỉ đọc) hoặc luôn lấy một bản sao phòng thủ (Không thay đổi). Đây là cách ổi ImmutableList làm điều đó.
Laurent Bourgault-Roy

1
@ LaurentBourgault-Roy: Có những lợi thế cho cả hai loại bất biến niêm phong và di truyền. Nếu một người không muốn cho phép một lớp dẫn xuất bất hợp pháp phá vỡ các bất biến của một người, các loại niêm phong có thể cung cấp sự bảo vệ chống lại điều đó trong khi các lớp kế thừa không cung cấp. Mặt khác, nó có thể có thể cho mã mà biết điều gì đó về dữ liệu mà nó nắm giữ để lưu trữ nó nhiều hơn gọn hơn sẽ là một loại mà không biết gì về nó. Ví dụ, hãy xem xét một loại Readable IndexedIntSequence bao gói một chuỗi các intphương thức getLength()getItemAt(int).
supercat

1
@ LaurentBourgault-Roy: Với một ReadableIndexedIntSequence, người ta có thể tạo ra một thể hiện của loại bất biến được hỗ trợ mảng bằng cách sao chép tất cả các mục vào một mảng, nhưng giả sử rằng một triển khai cụ thể chỉ trả về 16777216 ((long)index*index)>>24cho mỗi mục. Đó sẽ là một chuỗi số nguyên bất biến hợp pháp, nhưng sao chép nó vào một mảng sẽ là một sự lãng phí rất lớn về thời gian và bộ nhớ.
supercat

1
Tôi hoàn toàn đồng ý. Giải pháp của tôi cung cấp cho bạn tính chính xác (tối đa một điểm), nhưng để có được hiệu suất với tập dữ liệu lớn, bạn phải có cấu trúc và thiết kế bền bỉ cho sự bất biến ngay từ đầu. Đối với bộ sưu tập nhỏ mặc dù thỉnh thoảng bạn có thể lấy đi một bản sao bất biến. Tôi nhớ rằng Scala đã thực hiện một số phân tích về các chương trình khác nhau và nhận thấy rằng khoảng 90% danh sách được đưa ra là 10 mục hoặc ít hơn.
Laurent Bourgault-Roy

1
@ LaurentBourgault-Roy: Câu hỏi cơ bản là liệu người ta có tin tưởng mọi người không tạo ra các triển khai bị hỏng hoặc các lớp dẫn xuất hay không. Nếu có, và nếu các giao diện / lớp cơ sở cung cấp các phương thức asMutable / asImmutable, thì có thể cải thiện hiệu suất theo nhiều bậc độ lớn [ví dụ: so sánh chi phí gọi asImmutabletheo một thể hiện của chuỗi được xác định ở trên với chi phí xây dựng một bản sao được hỗ trợ mảng bất biến]. Tôi sẽ khẳng định rằng việc có các giao diện được xác định cho các mục đích như vậy có lẽ tốt hơn là cố gắng sử dụng các phương pháp tiếp cận đặc biệt; IMHO, lý do lớn nhất ...
supercat 23/12/13

15

Khung công tác bộ sưu tập Java cung cấp khả năng tạo một phiên bản chỉ đọc của một bộ sưu tập bằng sáu phương thức tĩnh trong lớp java.util.Collections :

Như ai đó đã chỉ ra trong các nhận xét cho câu hỏi ban đầu, các bộ sưu tập được trả về có thể không được coi là bất biến vì mặc dù các bộ sưu tập không thể được sửa đổi (không có thành viên nào có thể được thêm hoặc xóa khỏi bộ sưu tập đó), các đối tượng thực tế được tham chiếu bởi bộ sưu tập có thể được sửa đổi nếu loại đối tượng của họ cho phép nó.

Tuy nhiên, vấn đề này sẽ vẫn tồn tại bất kể mã trả về một đối tượng hay bộ sưu tập đối tượng không thể thay đổi. Nếu loại cho phép các đối tượng của nó bị đột biến, thì quyết định đó được đưa ra trong thiết kế của loại và tôi không thấy sự thay đổi đối với JCF có thể thay đổi điều đó như thế nào. Nếu bất biến là quan trọng, thì các thành viên của một bộ sưu tập nên thuộc loại bất biến.


4
Thiết kế của các bộ sưu tập không thể thay đổi sẽ được tăng cường đáng kể nếu các trình bao bọc bao gồm một dấu hiệu cho biết liệu thứ được bọc có bất biến hay không, và có immutableListcác phương pháp nhà máy sẽ trả về một trình bao bọc chỉ đọc xung quanh một bản sao của một bản sao danh sách trừ khi danh sách thông qua đã bất biến . Có thể dễ dàng tạo các kiểu do người dùng định nghĩa như vậy nhưng với một vấn đề: sẽ không có cách nào để joesCollections.immutableListphương thức nhận ra rằng nó không cần phải sao chép đối tượng được trả về fredsCollections.immutableList.
supercat

8

Đây là một câu hỏi rất hay. Tôi thích giải trí với ý tưởng rằng tất cả các mã được viết bằng java và chạy trên hàng triệu máy tính trên toàn thế giới, mỗi ngày, xung quanh đồng hồ, khoảng một nửa tổng số chu kỳ đồng hồ phải bị lãng phí không làm gì ngoài việc tạo ra các bản sao an toàn của các bộ sưu tập được trả về bởi các chức năng. (Và thu gom rác các bộ sưu tập này một phần nghìn giây sau khi tạo.)

Một tỷ lệ lập trình viên java nhận thức được sự tồn tại của unmodifiableCollection()họ các phương thức của Collectionslớp, nhưng ngay cả trong số họ, nhiều người không bận tâm đến nó.

Và tôi không thể đổ lỗi cho họ: một giao diện giả vờ đọc-ghi nhưng sẽ ném UnsupportedOperationExceptionnếu bạn phạm sai lầm khi gọi bất kỳ phương thức 'viết' nào của nó là một điều khá xấu xa!

Bây giờ, một giao diện giống như Collectionmà sẽ được thiếu add(), remove()clear()phương pháp này sẽ không phải là một "ImmutableCollection" giao diện; nó sẽ là một giao diện "UnmodifiableCollection". Như một vấn đề thực tế, không bao giờ có thể có giao diện "ImmutableCollection", bởi vì tính bất biến là bản chất của việc triển khai, không phải là một đặc điểm của giao diện. Tôi biết, điều đó không rõ ràng lắm; hãy để tôi giải thích.

Giả sử ai đó trao cho bạn một giao diện bộ sưu tập chỉ đọc như vậy; Có an toàn để chuyển nó sang một chủ đề khác? Nếu bạn biết chắc chắn rằng nó đại diện cho một bộ sưu tập thực sự bất biến, thì câu trả lời sẽ là "có"; thật không may, vì nó là một giao diện, bạn không biết nó được triển khai như thế nào, vì vậy câu trả lời phải là không : đối với tất cả những gì bạn biết, nó có thể là một quan điểm không thể thay đổi (đối với bạn) về một bộ sưu tập thực tế có thể thay đổi được, (giống như những gì bạn nhận được với Collections.unmodifiableCollection(),) vì vậy cố gắng đọc từ nó trong khi một luồng khác đang sửa đổi nó sẽ dẫn đến việc đọc dữ liệu bị hỏng.

Vì vậy, những gì bạn đã mô tả về cơ bản là một tập hợp các giao diện bộ sưu tập "Không thể thay đổi", nhưng "Không thể thay đổi". Điều quan trọng là phải hiểu rằng "Không thể thay đổi" đơn giản có nghĩa là bất kỳ ai có tham chiếu đến giao diện như vậy đều bị ngăn không cho sửa đổi bộ sưu tập cơ bản và chúng bị ngăn chặn đơn giản vì giao diện thiếu bất kỳ phương pháp sửa đổi nào, không phải vì bộ sưu tập cơ bản nhất thiết là bất biến. Bộ sưu tập cơ bản rất có thể là đột biến; bạn không có kiến ​​thức về, và không kiểm soát được điều đó.

Để có những bộ sưu tập bất biến, chúng sẽ phải là các lớp chứ không phải giao diện!

Các lớp bộ sưu tập bất biến này sẽ phải là cuối cùng, do đó, khi bạn được cung cấp một tham chiếu đến một bộ sưu tập như vậy, bạn biết chắc rằng nó sẽ hoạt động như một bộ sưu tập bất biến cho dù bạn hay bất kỳ ai có liên quan đến nó, có thể làm với nó

Vì vậy, để có một bộ sưu tập hoàn chỉnh trong java, (hoặc bất kỳ ngôn ngữ mệnh lệnh khai báo nào khác), chúng ta sẽ cần những điều sau đây:

  1. Một tập hợp các giao diện bộ sưu tập không thể thay đổi .

  2. Một tập hợp các giao diện bộ sưu tập có thể thay đổi , mở rộng những giao diện không thể thay đổi.

  3. Một tập hợp các lớp bộ sưu tập có thể thay đổi thực hiện các giao diện có thể thay đổi và bằng cách mở rộng cũng là các giao diện không thể thay đổi.

  4. Một tập hợp các lớp bộ sưu tập bất biến , thực hiện các giao diện không thể thay đổi, nhưng chủ yếu được truyền qua dưới dạng các lớp, để đảm bảo tính bất biến.

Tôi đã thực hiện tất cả những điều trên cho vui, và tôi đang sử dụng chúng trong các dự án, và chúng hoạt động như một cơ duyên.

Lý do tại sao chúng không phải là một phần của thời gian chạy java có lẽ là vì người ta nghĩ rằng điều này sẽ quá nhiều / quá phức tạp / quá khó hiểu.

Cá nhân, tôi nghĩ rằng những gì tôi mô tả ở trên thậm chí không đủ; một điều nữa dường như là cần thiết là một tập hợp các giao diện & lớp có thể thay đổi cho tính bất biến cấu trúc . (Điều này có thể đơn giản được gọi là "Cứng nhắc" vì tiền tố "StructaturalImmutable" quá dài.)


Điểm tốt. Hai chi tiết: 1. Các bộ sưu tập không thay đổi yêu cầu một số chữ ký phương thức nhất định, cụ thể (sử dụng Danh sách làm ví dụ): List<T> add(T t)- tất cả các phương thức "trình biến đổi" phải trả về một bộ sưu tập mới phản ánh sự thay đổi. 2. Để tốt hơn hoặc tồi tệ hơn, Giao diện thường đại diện cho một hợp đồng bên cạnh chữ ký. Nối tiếp là một giao diện như vậy. Tương tự, so sánh yêu cầu bạn thực hiện chính xác compareTo()phương pháp của mình để hoạt động chính xác và lý tưởng là tương thích với equals()hashCode().
GlenPeterson

Ồ, tôi thậm chí không có sự bất biến đột biến trong bản sao. Những gì tôi viết ở trên đề cập đến các bộ sưu tập đơn giản bất biến đơn giản mà thực sự không có phương pháp nào như thế add(). Nhưng tôi cho rằng nếu phương pháp mutator đã được bổ sung vào các lớp không thay đổi, sau đó họ sẽ cần phải trả lại cũng lớp không thay đổi. Vì vậy, nếu có một vấn đề ẩn giấu ở đó, tôi không thấy nó.
Mike Nakis

Việc triển khai của bạn có công khai không? Tôi nên đã hỏi điều này vài tháng trước. Dù sao, của tôi là: github.com/GlenKPeterson/UncleJim
GlenPeterson

4
Suppose someone hands you such a read-only collection interface; is it safe to pass it to another thread?Giả sử ai đó vượt qua bạn một ví dụ về giao diện bộ sưu tập có thể thay đổi. Có an toàn để gọi bất kỳ phương pháp trên nó? Bạn không biết rằng việc triển khai không lặp lại mãi mãi, ném ngoại lệ hoặc hoàn toàn không để ý đến hợp đồng của giao diện. Tại sao có một tiêu chuẩn kép đặc biệt cho các bộ sưu tập bất biến?
Doval

1
IMHO lý luận của bạn chống lại các giao diện có thể thay đổi là sai. Bạn có thể viết một triển khai có thể thay đổi của các giao diện bất biến, và sau đó nó bị hỏng. Chắc chắn rồi. Nhưng đó là lỗi của bạn khi bạn vi phạm hợp đồng. Chỉ cần ngừng làm điều đó. Không khác gì phá vỡ một SortedSetbằng cách phân lớp tập hợp với triển khai không tuân thủ. Hoặc bằng cách vượt qua một sự không nhất quán Comparable. Gần như bất cứ điều gì có thể bị phá vỡ nếu bạn muốn. Tôi đoán, đó là những gì @Doval có nghĩa là "tiêu chuẩn kép".
maaartinus

2

Các bộ sưu tập bất biến có thể được đệ quy sâu, so với nhau, và không hiệu quả một cách vô lý nếu sự bình đẳng đối tượng là bởi safeHash. Đây được gọi là rừng merkle. Nó có thể là trên mỗi bộ sưu tập hoặc trong các phần của chúng như cây AVL (tự cân bằng nhị phân) cho bản đồ được sắp xếp.

Trừ khi tất cả các đối tượng java trong các bộ sưu tập này có id duy nhất hoặc một số chuỗi bit để băm, bộ sưu tập không có gì để băm để đặt tên duy nhất.

Ví dụ: Trên máy tính xách tay 4x1.6ghz của tôi, tôi có thể chạy 200K sha256s mỗi giây với kích thước nhỏ nhất phù hợp với 1 chu kỳ băm (tối đa 55 byte), so với op op 500K hoặc op op 3M trong một hàm băm dài. 200K / log (bộ sưu tập Kích thước) bộ sưu tập mới mỗi giây đủ nhanh cho một số điều trong đó tính toàn vẹn dữ liệu và khả năng mở rộng toàn cầu ẩn danh là quan trọng.


-3

Hiệu suất. Bộ sưu tập theo bản chất của họ có thể rất lớn. Sao chép 1000 phần tử sang một cấu trúc mới với 1001 phần tử thay vì chèn một phần tử chỉ đơn giản là khủng khiếp.

Đồng thời. Nếu bạn có một số luồng đang chạy, họ có thể muốn có phiên bản hiện tại của bộ sưu tập chứ không phải phiên bản đã được thông qua 12 giờ trước khi chuỗi bắt đầu.

Lưu trữ. Với các đối tượng bất biến trong môi trường đa luồng, bạn có thể kết thúc với hàng chục bản sao của đối tượng "giống nhau" tại các điểm khác nhau trong vòng đời của nó. Không quan trọng đối với Lịch hoặc đối tượng Ngày nhưng khi bộ sưu tập 10.000 tiện ích này sẽ giết bạn.


12
Các bộ sưu tập không thay đổi chỉ yêu cầu sao chép nếu bạn không thể chia sẻ vì tính biến đổi lan tỏa như Java có. Đồng thời thường dễ dàng hơn với các bộ sưu tập bất biến vì chúng không yêu cầu khóa; và về khả năng hiển thị, bạn luôn có thể có một tham chiếu có thể thay đổi đến một bộ sưu tập bất biến (phổ biến trong OCaml). Với việc chia sẻ, cập nhật về cơ bản có thể miễn phí. Bạn có thể thực hiện phân bổ logarit nhiều hơn so với cấu trúc có thể thay đổi, nhưng khi cập nhật, nhiều tiểu dự án đã hết hạn có thể được giải phóng ngay lập tức hoặc được sử dụng lại, do đó bạn không nhất thiết phải có chi phí bộ nhớ cao hơn.
Jon Purdy

4
Vấn đề vợ chồng. Các bộ sưu tập trong Clojure và Scala đều bất biến, nhưng hỗ trợ các bản sao nhẹ. Thêm phần tử 1001 có nghĩa là sao chép ít hơn 33 phần tử, cộng với tạo một vài gợi ý mới. Nếu bạn chia sẻ một bộ sưu tập có thể thay đổi giữa các chủ đề, bạn có tất cả các loại vấn đề đồng bộ hóa khi bạn thay đổi nó. Các hoạt động như "remove ()" là ác mộng. Ngoài ra, các bộ sưu tập bất biến có thể được xây dựng một cách đột biến, sau đó được sao chép một lần vào phiên bản bất biến an toàn để chia sẻ trên các luồng.
GlenPeterson

4
Sử dụng đồng thời như một đối số chống lại sự bất biến là không bình thường. Bản sao cũng vậy.
Tom Hawtin - tackline

4
Một chút đánh giá về các phiếu giảm ở đây. OP đã hỏi tại sao họ không thực hiện các bộ sưu tập bất biến, và, tôi đã cung cấp một câu trả lời được xem xét cho câu hỏi. Có lẽ câu trả lời duy nhất được chấp nhận trong số những người có ý thức thời trang là "bởi vì họ đã phạm sai lầm". Tôi thực sự có một số kinh nghiệm với việc này phải cấu trúc lại các đoạn mã lớn bằng cách sử dụng lớp BigDecimal tuyệt vời khác hoàn toàn vì tính không hoàn hảo do tính bất biến 512 lần so với việc sử dụng gấp đôi cộng với một số sai lầm xung quanh để sửa các số thập phân.
James Anderson

3
@JamesAnderson: Vấn đề của tôi với câu trả lời của bạn: "Hiệu suất" - bạn có thể nói rằng các bộ sưu tập bất biến ngoài đời thực luôn thực hiện một số hình thức chia sẻ và tái sử dụng để tránh chính xác vấn đề bạn mô tả. "Đồng thời" - đối số sôi nổi thành "Nếu bạn muốn khả năng biến đổi, thì một đối tượng bất biến không hoạt động." Ý tôi là nếu có một khái niệm "phiên bản mới nhất của cùng một thứ", thì một cái gì đó cần phải biến đổi, hoặc chính nó, hoặc thứ gì đó sở hữu thứ đó. Và trong "Storage", bạn dường như nói rằng khả năng biến đổi đôi khi không được mong muốn.
jhominal
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.