Java8 스트림에서 객체를 수정 / 업데이트 할 수 있습니까? 예를 들어. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
답변:
예, 스트림 내부의 객체 상태를 수정할 수 있지만 대부분의 경우 스트림 소스 상태를 수정하지 않아야 합니다. 에서 비 간섭 우리가 읽을 수 스트림 패키지 문서의 섹션 :
대부분의 데이터 소스에서 간섭 방지 는 스트림 파이프 라인 실행 중에 데이터 소스가 전혀 수정되지 않도록하는 것을 의미합니다 . 이에 대한 주목할만한 예외는 소스가 동시 수정을 처리하도록 특별히 설계된 동시 컬렉션 인 스트림입니다. 동시 스트림 소스는 특성을
Spliterator
보고하는 소스CONCURRENT
입니다.
그래서 괜찮아
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
그러나 이것은 대부분의 경우에 아닙니다
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
ConcurrentModificationException
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
반복하는 동안 목록 수정 (새 요소 제거 / 추가)을 방지하고 스트림과 for-each 루프가 모두 사용하는 것을 방지 해야 합니다. for(int i=0; ..; ..)
이 문제가없는 간단한 루프를 사용해 볼 수 있습니다 (그러나 다른 스레드가 목록을 수정할 때 중단되지는 않습니다). 당신은 또한 같은 방법을 사용할 수 있습니다 list.removeAll(Collection)
, list.removeIf(Predicate)
. 또한 java.util.Collections
클래스에는 addAll(CollectionOfNewElements,list)
.
filter()
기능적인 방법은 다음과 같습니다.
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]
}
}
이 솔루션에서 원래 목록은 수정되지 않지만 이전 목록과 동일한 변수에서 액세스 할 수있는 새 목록에 예상 결과를 포함해야합니다.
ConcurrentModificationException을 제거 하려면 CopyOnWriteArrayList를 사용하십시오.
List<Something>
CopyOnWriteArrayList인지 알 수 없습니다. 따라서 이것은 평범한 의견과 비슷하지만 실제 답변은 아닙니다.
예, 다음과 같이 경우에 따라 목록의 개체 값을 수정하거나 업데이트 할 수 있습니다.
users.stream().forEach(u -> u.setProperty("some_value"))
그러나 위의 문은 소스 개체를 업데이트합니다. 대부분의 경우 허용되지 않을 수 있습니다.
다행히 다음과 같은 다른 방법이 있습니다.
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
이전 목록을 방해하지 않고 업데이트 된 목록을 반환합니다.
이전에 언급했듯이 원래 목록은 수정할 수 없지만 항목을 새 목록으로 스트리밍, 수정 및 수집 할 수 있습니다. 다음은 문자열 요소를 수정하는 방법에 대한 간단한 예입니다.
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]
}
}
조금 늦을 수 있습니다. 그러나 여기에 사용법 중 하나가 있습니다. 이것은 파일 수를 가져옵니다.
메모리에 대한 포인터 (이 경우 새 obj)를 만들고 개체의 속성을 수정합니다. Java 8 스트림은 포인터 자체를 수정할 수 없으므로 변수로 카운트 만 선언하고 스트림 내에서 증가하려고하면 작동하지 않으며 처음에 컴파일러 예외가 발생합니다.
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 + "]";
}
}