2012-06-19 31 views
7

Tôi muốn lặp qua một tập hợp nhưng nội dung của tập hợp sẽ sửa đổi trong khi lặp lại. Tôi muốn lặp qua tập hợp ban đầu tại thời điểm trình vòng lặp được tạo và không lặp qua bất kỳ phần tử mới nào được thêm vào tập hợp. Sao có thể như thế được? Đây có phải là hành vi mặc định của bộ hoặc làm cách nào tôi có thể thực hiện việc này?Java: Lặp lại một tập trong khi nội dung của bộ đang được sửa đổi

Một cách tôi có thể nghĩ là để có được một bộ mới từ bộ ban đầu mà sẽ không được sửa đổi nhưng điều này có vẻ không phù hợp và phải có một giải pháp tốt hơn.

+2

Cách bạn đề xuất có vẻ ổn. – assylias

+1

Để làm rõ - đây có phải là một luồng đơn hay đa luồng không? – templatetypedef

+0

Đa luồng. Một chuỗi đang lặp lại, một chuỗi khác đang thay đổi tập hợp. Tôi không muốn tạm dừng một trong hai luồng cho các vấn đề hiệu suất. – nomel7

Trả lời

8

Chụp nhanh tập hợp có vẻ giống như giải pháp phù hợp với tôi, nếu bạn muốn đảm bảo bạn không thấy bất kỳ phần tử mới nào. Có một số bộ như ConcurrentSkipListSet sẽ cho phép bạn tiếp tục lặp lại, nhưng tôi không thể xem bất kỳ sự đảm bảo nào về hành vi của một trình lặp trong điều kiện nhìn thấy các phần tử mới.

CHỈNH SỬA: CopyOnWriteArraySet có các yêu cầu bạn cần, nhưng ghi là tốn kém, có vẻ như nó không phù hợp với bạn.

Đó là những bộ duy nhất tôi có thể thấy trong java.util.concurrent, là gói tự nhiên cho các bộ sưu tập đó. Việc sao chép vẫn có thể đơn giản hơn :)

+0

Nó phụ thuộc. Nếu snapshot _isn't_ yêu cầu (không ai xảy ra để chèn trong khi bạn đang lặp lại) CopyOnWriteArraySet sẽ nhanh hơn. Vì vậy, nó phụ thuộc vào mức độ thường xuyên có những va chạm thực tế. – user949300

+0

@ user949300: Tôi không biết chi tiết khi CopyOnWriteArraySet thực sự yêu cầu lấy một bản sao, nhưng tài liệu tuyên bố nó "thường" (bất kể điều đó có nghĩa là gì). Nó chắc chắn sẽ tốt đẹp nếu nó chỉ sao chép khi thực sự, thực sự cần thiết ... –

7

EDIT: Câu trả lời này được thiết kế cho một trường hợp đơn luồng, vì tôi đã giải thích câu hỏi của OP như tránh sự hài hòa hơn là tránh các vấn đề đa luồng. Tôi để lại câu trả lời này ở đây trong trường hợp nó kết thúc là hữu ích cho bất cứ ai trong tương lai đang sử dụng một cách tiếp cận đơn luồng.

Không có cách nào trực tiếp để thực hiện việc này. Tuy nhiên, một tùy chọn khá tốt là có hai bộ - tập hợp chính, mà bạn lặp lại, và một bộ phụ mà bạn chèn tất cả các phần tử mới cần được thêm vào. Sau đó bạn có thể lặp qua tập hợp chính, sau đó khi đã hoàn tất, hãy sử dụng addAll để thêm tất cả các phần tử mới vào tập hợp chính.

Ví dụ:

Set<T> masterSet = /* ... */ 

Set<T> newElems = /* ... */ 
for (T obj: masterSet) { 
    /* ... do something to each object ... */ 
} 

masterSet.addAll(newElems); 

Hope this helps!

+1

Tôi thích phương pháp này vì (a) nó tạo ra ít vật thể tạm thời hơn là sao chép toàn bộ tập gốc và (b) nó tránh được rất nhiều chi phí đồng thời mà bạn có thể không cần. 'Tôi không thể thấy bất kỳ sự bảo đảm nào về hành vi của một trình vòng lặp trong việc xem các phần tử mới' có thể là một vấn đề đối với' ConcurrentSkipListSet' –

+0

Tôi không chắc chắn cách thức này có thể hoạt động. Làm thế nào để thread thứ 2 biết rằng nó phải thêm vào newElems, không phải để masterSet ??? Và, nếu bạn KHÔNG lặp lại, ai biết sau đó hợp nhất newElems vào masterSet ??? – user949300

+0

@ user949300- Tôi sẽ giả định bất kỳ mã nào có nghĩa là thêm các phần tử vào tập hợp có thể biết những yếu tố mới được đặt là gì. Cũng lưu ý rằng câu hỏi của OP không nói gì về đa luồng; Tôi nghĩ vấn đề là sự hài hòa hơn là đồng thời. Nó sẽ rất dễ dàng để truyền đạt thông tin mới này vào các chủ đề khác nếu chúng tồn tại. – templatetypedef

2

Làm một bản sao của Set giải pháp thanh lịch.

Set<Obj> copyOfObjs = new HashSet<Obj>(originalSet); 
for(Obj original : originalSet) { 
    //add some more stuff to copyOfObjs 
} 
0

Bây giờ OP đã làm rõ các yêu cầu, các giải pháp

  1. Sao chép các thiết lập trước khi lặp lại
  2. Sử dụng CopyOnWriteArraySet
  3. Viết mã tùy chỉnh của riêng bạn và cố gắng để được thông minh hơn rất nhiều của những người thông minh.

Hạn chế của # 1 là bạn luôn sao chép tập hợp ngay cả khi nó không cần thiết (ví dụ: nếu không có chèn thực sự xảy ra trong khi bạn đang lặp lại), tôi khuyên bạn nên chọn tùy chọn # 2. chèn đang gây ra một vấn đề hiệu suất thực sự.

0

Như những người khác đã đề xuất ở đây, không có giải pháp tối ưu cho những gì bạn tìm kiếm. Tất cả phụ thuộc vào trường hợp sử dụng của ứng dụng của bạn hoặc việc sử dụng tập hợp
Vì Set là một giao diện bạn có thể định nghĩa lớp DoubleSet của riêng bạn sẽ triển khai Set và giả sử sẽ sử dụng hai trường HashSet.
Khi bạn truy xuất một trình lặp, bạn nên đánh dấu một trong các bộ này thành "chế độ chỉ tương tác", vì vậy phương thức bổ sung sẽ chỉ thêm vào tập hợp khác


Tôi vẫn còn mới với Stackoverlflow, vì vậy tôi cần hiểu làm thế nào để nhúng mã trong câu trả lời của tôi :(nhưng nói chung bạn nên có một lớp được gọi là MySet (Generic chung loại T) thực hiện Set của loại chung T.
Bạn cần phải thực hiện tất cả các phương pháp, và có hai lĩnh vực - một được gọi là iterationSet và cái kia được gọi là insertionSet
Bạn cũng sẽ có một trường boolean cho biết nếu để chèn vào hai bộ hay không.Khi phương thức iterator() được gọi, boolean này nên được đặt thành false, có nghĩa là bạn nên chèn chỉ cho bộ chèn.
Bạn nên có một phương thức sẽ đồng bộ hóa nội dung của hai tập khi bạn đã hoàn tất với trình lặp.
Tôi hy vọng tôi đã rõ ràng

Các vấn đề liên quan