Hãy để tôi đưa ra một vài ví dụ với một số lựa chọn thay thế để tránh a ConcurrentModificationException
.
Giả sử chúng ta có bộ sưu tập sách sau đây
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Thu thập và loại bỏ
Kỹ thuật đầu tiên bao gồm thu thập tất cả các đối tượng mà chúng ta muốn xóa (ví dụ: sử dụng vòng lặp nâng cao) và sau khi hoàn thành việc lặp lại, chúng ta xóa tất cả các đối tượng tìm thấy.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
Điều này giả sử rằng thao tác bạn muốn làm là "xóa".
Nếu bạn muốn "thêm" phương pháp này cũng sẽ hoạt động, nhưng tôi cho rằng bạn sẽ lặp qua một bộ sưu tập khác để xác định những yếu tố nào bạn muốn thêm vào bộ sưu tập thứ hai và sau đó đưa ra một addAll
phương thức ở cuối.
Sử dụng ListIterator
Nếu bạn đang làm việc với các danh sách, một kỹ thuật khác bao gồm việc sử dụng một ListIterator
hỗ trợ để loại bỏ và thêm các mục trong quá trình lặp.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Một lần nữa, tôi đã sử dụng phương thức "loại bỏ" trong ví dụ ở trên, đây là điều mà câu hỏi của bạn dường như ngụ ý, nhưng bạn cũng có thể sử dụng add
phương thức của nó để thêm các phần tử mới trong quá trình lặp.
Sử dụng JDK> = 8
Đối với những người làm việc với Java 8 hoặc các phiên bản cao cấp, có một vài kỹ thuật khác mà bạn có thể sử dụng để tận dụng lợi thế của nó.
Bạn có thể sử dụng removeIf
phương thức mới trong Collection
lớp cơ sở:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Hoặc sử dụng API luồng mới:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
Trong trường hợp cuối cùng này, để lọc các phần tử ra khỏi bộ sưu tập, bạn chỉ định lại tham chiếu ban đầu cho bộ sưu tập được lọc (nghĩa là books = filtered
) hoặc sử dụng bộ sưu tập được lọc cho removeAll
các phần tử tìm thấy từ bộ sưu tập gốc (nghĩa là books.removeAll(filtered)
).
Sử dụng danh sách con hoặc tập hợp con
Có những lựa chọn thay thế khác là tốt. Nếu danh sách được sắp xếp và bạn muốn xóa các phần tử liên tiếp, bạn có thể tạo một danh sách phụ và sau đó xóa nó:
books.subList(0,5).clear();
Vì danh sách con được hỗ trợ bởi danh sách ban đầu, đây sẽ là một cách hiệu quả để loại bỏ tập hợp con các phần tử này.
Một cái gì đó tương tự có thể đạt được với các bộ được sắp xếp bằng NavigableSet.subSet
phương pháp hoặc bất kỳ phương pháp cắt nào được cung cấp ở đó.
Cân nhắc:
Phương pháp bạn sử dụng có thể phụ thuộc vào những gì bạn định làm
- Bộ sưu tập và
removeAl
kỹ thuật hoạt động với mọi Bộ sưu tập (Bộ sưu tập, Danh sách, Bộ, v.v.).
- Các
ListIterator
kỹ thuật rõ ràng là chỉ hoạt động với danh sách, với điều kiện cho họ ListIterator
thực hiện cung cấp hỗ trợ cho tiện ích và loại bỏ các hoạt động.
- Cách
Iterator
tiếp cận sẽ hoạt động với bất kỳ loại bộ sưu tập nào, nhưng nó chỉ hỗ trợ loại bỏ các hoạt động.
- Với
ListIterator
/ Iterator
phương pháp tiếp cận, lợi thế rõ ràng là không phải sao chép bất cứ điều gì kể từ khi chúng tôi xóa khi chúng tôi lặp lại. Vì vậy, điều này rất hiệu quả.
- Ví dụ về luồng JDK 8 không thực sự loại bỏ bất cứ thứ gì, nhưng tìm kiếm các phần tử mong muốn và sau đó chúng tôi đã thay thế tham chiếu bộ sưu tập ban đầu bằng cái mới và để cái cũ được thu gom rác. Vì vậy, chúng tôi chỉ lặp lại một lần trong bộ sưu tập và điều đó sẽ hiệu quả.
- Trong việc thu thập và
removeAll
tiếp cận nhược điểm là chúng ta phải lặp lại hai lần. Đầu tiên chúng tôi lặp lại trong vòng lặp tìm kiếm một đối tượng phù hợp với tiêu chí loại bỏ của chúng tôi và khi chúng tôi đã tìm thấy nó, chúng tôi yêu cầu xóa nó khỏi bộ sưu tập ban đầu, điều này có nghĩa là một công việc lặp lại thứ hai để tìm kiếm mục này để xóa nó.
- Tôi nghĩ điều đáng nói là phương thức gỡ bỏ của
Iterator
giao diện được đánh dấu là "tùy chọn" trong Javadocs, điều đó có nghĩa là có thể có các Iterator
triển khai ném UnsupportedOperationException
nếu chúng ta gọi phương thức gỡ bỏ. Như vậy, tôi muốn nói rằng phương pháp này kém an toàn hơn các phương pháp khác nếu chúng tôi không thể đảm bảo hỗ trợ lặp để loại bỏ các phần tử.