Trong các luồng Java8, tôi có được phép sửa đổi / cập nhật các đối tượng bên trong không? Ví dụ. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Trong các luồng Java8, tôi có được phép sửa đổi / cập nhật các đối tượng bên trong không? Ví dụ. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Câu trả lời:
Có, bạn có thể sửa đổi trạng thái của các đối tượng bên trong luồng của mình, nhưng thường thì bạn nên tránh sửa đổi trạng thái của nguồn luồng. Từ phần không can thiệp của tài liệu gói luồng, chúng ta có thể đọc rằng:
Đối với hầu hết các nguồn dữ liệu, việc ngăn chặn sự can thiệp có nghĩa là đảm bảo rằng nguồn dữ liệu không bị sửa đổi trong quá trình thực hiện đường truyền luồng. Ngoại lệ đáng chú ý đối với điều này là các luồng có nguồn là các tập hợp đồng thời, được thiết kế đặc biệt để xử lý sửa đổi đồng thời. Nguồn luồng đồng thời là những nguồn có đặc điểm
Spliterator
báo cáoCONCURRENT
.
Vì vậy, điều này là OK
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
nhưng điều này trong hầu hết các trường hợp không phải là
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
và có thể ném ConcurrentModificationException
hoặc thậm chí các ngoại lệ bất ngờ khác như NPE:
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
list.stream()
.filter(i -> i > 5)
.forEach(i -> list.remove(i)); //throws NullPointerException
Iterator
điều này ngăn cản việc sửa đổi danh sách (loại bỏ / thêm phần tử mới) trong khi lặp và cả luồng và cho từng vòng đều đang sử dụng nó. Bạn có thể thử sử dụng vòng lặp đơn giản như for(int i=0; ..; ..)
không có vấn đề này (nhưng sẽ không dừng bạn khi chuỗi khác sẽ sửa đổi danh sách của bạn). Bạn cũng có thể sử dụng các phương pháp như list.removeAll(Collection)
, list.removeIf(Predicate)
. Ngoài ra java.util.Collections
lớp có một số phương thức có thể hữu ích như addAll(CollectionOfNewElements,list)
.
filter()
Cách chức năng sẽ là:
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTestRun {
public static void main(String[] args) {
List<String> lines = Arrays.asList("a", "b", "c");
System.out.println(lines); // [a, b, c]
Predicate<? super String> predicate = value -> "b".equals(value);
lines = lines.stream().filter(predicate.negate()).collect(toList());
System.out.println(lines); // [a, c]
}
}
Trong giải pháp này, danh sách ban đầu không được sửa đổi, nhưng phải chứa kết quả mong đợi của bạn trong một danh sách mới có thể truy cập theo cùng một biến với danh sách cũ
Để thực hiện sửa đổi cấu trúc trên nguồn của luồng, như Pshemo đã đề cập trong câu trả lời của mình, một giải pháp là tạo một phiên bản Collection
tương tự mới ArrayList
với các mục bên trong danh sách chính của bạn; lặp qua danh sách mới và thực hiện các thao tác trên danh sách chính.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Để thoát khỏi ConcurrentModificationException, hãy sử dụng CopyOnWriteArrayList
List<Something>
- bạn không biết đó có phải là CopyOnWriteArrayList hay không. Vì vậy, điều này giống như một bình luận tầm thường, nhưng không phải là một câu trả lời thực sự.
Thay vì tạo ra những điều kỳ lạ, bạn có thể chỉ filter()
và sau đó map()
là kết quả của bạn.
Điều này dễ đọc hơn và chắc chắn. Luồng sẽ thực hiện nó chỉ trong một vòng lặp.
Có, bạn cũng có thể sửa đổi hoặc cập nhật giá trị của các đối tượng trong danh sách trong trường hợp của mình:
users.stream().forEach(u -> u.setProperty("some_value"))
Tuy nhiên, câu lệnh trên sẽ cập nhật các đối tượng nguồn. Điều này có thể không được chấp nhận trong hầu hết các trường hợp.
May mắn thay, chúng tôi có một cách khác như:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
Điều này sẽ trả về một danh sách đã cập nhật mà không cản trở danh sách cũ.
Như nó đã được đề cập trước đây - bạn không thể sửa đổi danh sách ban đầu, nhưng bạn có thể phát trực tuyến, sửa đổi và thu thập các mục vào danh sách mới. Đây là ví dụ đơn giản về cách sửa đổi phần tử chuỗi.
public class StreamTest {
@Test
public void replaceInsideStream() {
List<String> list = Arrays.asList("test1", "test2_attr", "test3");
List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
System.out.println("Output: " + output); // Output: [test1, test2, test3]
}
}
Bạn có thể sử dụng removeIf
để xóa dữ liệu khỏi danh sách một cách có điều kiện.
Vd: - Muốn xóa tất cả các số chẵn trong một danh sách, bạn có thể thực hiện như sau.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Điều này có thể là một chút muộn. Nhưng đây là một trong những cách sử dụng. Điều này để đếm số lượng tệp.
Tạo một con trỏ tới bộ nhớ (một đối tượng mới trong trường hợp này) và sửa đổi thuộc tính của đối tượng. Luồng Java 8 không cho phép sửa đổi chính con trỏ và do đó nếu bạn khai báo chỉ được tính là một biến và cố gắng tăng lên trong luồng, nó sẽ không bao giờ hoạt động và ném một ngoại lệ trình biên dịch ngay từ đầu
Path path = Paths.get("/Users/XXXX/static/test.txt");
Count c = new Count();
c.setCount(0);
Files.lines(path).forEach(item -> {
c.setCount(c.getCount()+1);
System.out.println(item);});
System.out.println("line count,"+c);
public static class Count{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Count [count=" + count + "]";
}
}