[java] 반복하는 동안 Java8의 스트림 내에서 객체 수정

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));
//^^^^^                       ^^^^^^^^^^^^

ConcurrentModificationExceptionNPE와 같은 예상치 못한 예외가 발생할 수 있습니다 .

List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .forEach(i -> list.remove(i));  //throws NullPointerException


답변

기능적인 방법은 다음과 같습니다.

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]
    }
}

이 솔루션에서 원래 목록은 수정되지 않지만 이전 목록과 동일한 변수에서 액세스 할 수있는 새 목록에 예상 결과를 포함해야합니다.


답변

Pshemo가 답변에서 언급했듯이 스트림 소스에 대한 구조적 수정을 수행하려면 한 가지 해결책은 기본 목록 내의 항목 을 사용하여 Collection유사한 새 인스턴스를 만드는 것 ArrayList입니다. 새 목록을 반복하고 기본 목록에서 작업을 수행합니다.

new ArrayList<>(users).stream().forEach(u -> users.remove(u));


답변

ConcurrentModificationException을 제거 하려면 CopyOnWriteArrayList를 사용하십시오.


답변

대신에 이상한 물건을 만드는, 당신은 할 수있는 filter()한 후 map()당신의 결과.

이것은 훨씬 더 읽기 쉽고 확실합니다. 스트림은 단 하나의 루프로 만들 것입니다.


답변

예, 다음과 같이 경우에 따라 목록의 개체 값을 수정하거나 업데이트 할 수 있습니다.

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]
    }
}