Java8 스트림 요소를 기존 목록에 추가하는 방법
Javadoc of Collector는 스트림 요소를 새 목록으로 수집하는 방법을 보여 줍니다.기존 Array List에 결과를 추가하는 단일 라이너가 있습니까?
주의: nosid의 답변은 기존 컬렉션에 추가하는 방법을 보여줍니다.forEachOrdered()
이는 기존 컬렉션을 변환하는 데 유용하고 효과적인 기술입니다.은 '왜 안' 입니다.Collector
기존 컬렉션을 변환합니다.
간단히 말하면, 적어도 일반적으로는 그렇지 않습니다.Collector
기존 컬렉션을 변경합니다.
그 이유는 컬렉터가 스레드 세이프가 아닌 컬렉션에서도 병렬 처리를 지원하도록 설계되어 있기 때문입니다.이 방법은 중간 결과의 자체 집합에서 각 스레드가 독립적으로 작동하도록 하는 것입니다.은, 「」를 하는 것입니다.Collector.supplier()
매번 새로운 컬렉션을 반환해야 합니다.
그런 다음 이러한 중간 결과 컬렉션은 단일 결과 컬렉션이 될 때까지 다시 스레드 바인딩 방식으로 병합됩니다.입니다.collect()
★★★★★★ 。
Balder와 아시리아가 제안한 몇 가지 답변은 다음과 같습니다.Collectors.toCollection()
새 목록이 아닌 기존 목록을 반환하는 공급업체를 통과해야 합니다.이는 공급업체의 요구 사항을 위반하는 것으로, 매번 빈 새 컬렉션을 반환해야 합니다.
이는 답변의 예에서 알 수 있듯이 단순한 경우에 유효합니다.그러나 스트림이 병렬로 실행되면 특히 실패합니다.(라이브러리의 미래 버전은 예기치 않은 방식으로 바뀌어 순차적인 경우에도 실패할 수 있습니다.)
간단한 예를 들어 보겠습니다.
List<String> destList = new ArrayList<>(Arrays.asList("foo"));
List<String> newList = Arrays.asList("0", "1", "2", "3", "4", "5");
newList.parallelStream()
.collect(Collectors.toCollection(() -> destList));
System.out.println(destList);
내가 이 프로그램을 실행하면, 나는 종종 이 프로그램을 실행한다.ArrayIndexOutOfBoundsException
의 스레드가 때문입니다.ArrayList
구조 , 「」를 참조해 주세요.요?
List<String> destList =
Collections.synchronizedList(new ArrayList<>(Arrays.asList("foo")));
이것은 더 이상 예외 없이 실패하지 않습니다.그러나 기대했던 결과 대신:
[foo, 0, 1, 2, 3]
다음과 같은 이상한 결과를 낳습니다.
[foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0]
이는 위에서 설명한 스레드 제한 누적/머지 작업의 결과입니다.병렬 스트림을 사용하면 각 스레드는 중간 축적을 위한 자체 컬렉션을 얻기 위해 공급업체에 연락합니다.동일한 컬렉션을 반환하는 공급업체를 전달하면 각 스레드가 해당 컬렉션에 결과를 추가합니다.스레드 사이에 순서가 없기 때문에 임의의 순서로 결과가 추가됩니다.
그런 다음 이러한 중간 컬렉션이 병합되면 기본적으로 목록이 자체와 병합됩니다.는 lists음음 음음음음 lists lists 、 lists 。List.addAll()
작업 중에 소스 컬렉션이 변경되면 결과가 정의되지 않음을 나타냅니다. 「」는,ArrayList.addAll()
어레이 카피 조작을 실시하기 때문에, 자기 자신을 복제하게 됩니다.(이 전혀 수 있습니다.) ('목록'의 실장에서는 동작이 전혀 다를 수 있습니다.)어쨌든, 이것은 행선지의 이상한 결과와 중복된 요소를 설명해 줍니다.
"내 스트림을 순차적으로 실행해 보겠습니다."라고 말한 후 다음과 같이 코드를 작성할 수 있습니다.
stream.collect(Collectors.toCollection(() -> existingList))
어쨌든 저는 이걸 하지 말 것을 권합니다.스트림을 제어하면 병렬로 실행되지 않을 수 있습니다.컬렉션이 아닌 스트림이 전달되는 프로그래밍 스타일이 등장할 것으로 기대하고 있습니다.누군가 당신에게 스트림을 건네주고 이 코드를 사용하면 스트림이 평행하게 되면 실패할 것입니다.더 나쁜 것은 누군가가 당신에게 연속적인 스트림을 건네주면 이 코드는 한동안 정상적으로 작동하고 모든 테스트에 합격하는 등입니다.그 후 어느 정도 시간이 지나면 시스템의 다른 곳에서 병렬 스트림을 사용하도록 코드가 변경되어 코드가 끊어질 수 있습니다.
, 꼭 .sequential()
다음 코드를 사용하기 전에 모든 스트림에서 다음을 수행합니다.
stream.sequential().collect(Collectors.toCollection(() -> existingList))
물론, 이 조작은 매번 잊지 않습니다. :-) 하고 있다고 칩시다.그 후, 퍼포먼스 팀은, 세심하게 설계된 병행 실장에서는, 왜 스피드가 향상되지 않는지를 궁금해하게 됩니다.그리고 다시 한 번 그들은 전체 스트림을 순차적으로 실행하도록 강요하는 코드까지 추적할 것입니다.
하지 마세요.
내가 볼 수 있는 한, 지금까지의 다른 모든 답변은 수집기를 사용하여 기존 스트림에 요소를 추가했습니다.그러나 더 짧은 솔루션이 있으며 순차 스트림과 병렬 스트림 모두에서 작동합니다.EachOrder에 대한 방법을 방법 참조와 함께 사용할 수 있습니다.
List<String> source = ...;
List<Integer> target = ...;
source.stream()
.map(String::length)
.forEachOrdered(target::add);
유일한 제약사항은 스트림의 소스가 처리되는 동안에는 스트림의 소스를 변경할 수 없기 때문에 소스와 타겟이 서로 다른 목록이라는 점입니다.
이 솔루션은 순차 스트림과 병렬 스트림 모두에서 작동합니다.그러나 동시성으로 인한 이점은 없습니다.forEachOrder에 전달된 메서드 참조는 항상 순차적으로 실행됩니다.
간단히 말하면, 「아니오」(또는 「아니오」)입니다.편집: 예, 가능합니다(아래 아시리아스의 답변 참조). 계속 읽어보십시오.EDIT2: 하지만 Stuart Marks의 답변을 참고하십시오.
더 긴 답변:
Java 8에서 이러한 구조의 목적은 기능 프로그래밍의 몇 가지 개념을 언어에 도입하는 것입니다. 기능 프로그래밍에서 데이터 구조는 일반적으로 수정되지 않고 맵, 필터, 접기/축소 등의 변환을 통해 오래된 것으로부터 새로운 구조가 생성됩니다.
이전 목록을 수정해야 할 경우 매핑된 항목을 새 목록으로 수집하기만 하면 됩니다.
final List<Integer> newList = list.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
그리고 나서 한다.list.addAll(newList)
- 다시 말씀드리지만, 꼭 해야 한다면요.
(또는 이전 목록과 새 목록을 연결한 새 목록을 작성하여 다시 할당한다.)list
variable: 이것은 FP의 정신에서는addAll
)
API에 대해서: API에 의해 허용되지만(다시 한 번 아시리아스의 답변을 참조), 적어도 일반적으로는 피하도록 해야 합니다.패러다임(FP)과 싸우지 말고 배우려고 노력하는 것이 좋습니다(Java는 일반적으로 FP 언어가 아니지만). 꼭 필요한 경우에만 "더 더러운" 전술에 의지하는 것이 좋습니다.
매우 긴 답변: (예: 제안대로 실제로 FP 인트로/북을 찾아 읽는 노력을 포함한다면)
기존 목록을 수정하는 것이 일반적으로 좋지 않은 방법이며, 로컬 변수를 수정하는 경우나 알고리즘이 짧거나 사소한 코드 유지보수가 불가능한 경우를 제외하고 유지보수가 어려운 코드를 찾으려면 기능 프로그래밍(수백 개)에 대한 적절한 설명을 찾아 읽기를 시작하십시오.「미리보기」의 설명은, 다음과 같습니다.이 설명은 수학적으로 보다 건전하고, (프로그램의 대부분의 부분에서) 데이터를 수정하지 않는 것을 추론하기 쉬워집니다.또, 낡은 스타일의 명령적 사고에서 벗어나면, 보다 높은 레벨로, 보다 기술적인(및 인간 친화적인) 프로그램 로직의 정의를 낳습니다.
Erik Kaplun은 이미 매우 좋은 이유를 제시했는데, 왜 스트림의 요소를 기존 목록에 모으고 싶지 않은지 그 이유를 제시했습니다.
어쨌든, 이 기능이 꼭 필요한 경우는, 다음의 원라이너를 사용할 수 있습니다.
그러나 다른 답변에서 지적된 바와 같이, 절대로 이렇게 해서는 안 됩니다. 특히 스트림이 병렬 스트림일 가능성이 있는 경우에는 자신의 책임 하에 사용하면 안 됩니다.
list.stream().collect(Collectors.toCollection(() -> myExistingList));
.Collectors.toList()
아온온다
데모를 소개합니다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Reference {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list);
// Just collect even numbers and start referring the new list as the original one.
list = list.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(list);
}
}
새로 작성한 요소를 원래 목록에 한 줄로 추가하는 방법은 다음과 같습니다.
List<Integer> list = ...;
// add even numbers from the list to the list again.
list.addAll(list.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList())
);
이것이 기능 프로그래밍 패러다임이 제공하는 것입니다.
이전 목록과 새 목록을 스트림으로 연결하고 결과를 대상 목록에 저장합니다.병렬로도 잘 작동합니다.
Stuart Marks가 제시한 답변의 예를 들어 보겠습니다.
List<String> destList = Arrays.asList("foo");
List<String> newList = Arrays.asList("0", "1", "2", "3", "4", "5");
destList = Stream.concat(destList.stream(), newList.stream()).parallel()
.collect(Collectors.toList());
System.out.println(destList);
//output: [foo, 0, 1, 2, 3, 4, 5]
도움이 됐으면 좋겠다.
기존 목록이 있으며 이 액티비티에 Java 8을 사용한다고 가정합니다.
import java.util.*;
import java.util.stream.Collectors;
public class AddingArray {
public void addArrayInList(){
List<Integer> list = Arrays.asList(3, 7, 9);
// And we have an array of Integer type
int nums[] = {4, 6, 7};
//Now lets add them all in list
// converting array to a list through stream and adding that list to previous list
list.addAll(Arrays.stream(nums).map(num ->
num).boxed().collect(Collectors.toList()));
}
}
`
targetList = sourceList.stream().flatmap(List::stream).collect(Collectors.toList());
언급URL : https://stackoverflow.com/questions/22753755/how-to-add-elements-of-a-java8-stream-into-an-existing-list
'source' 카테고리의 다른 글
cURL을 사용하여 베어러 토큰을 설정하는 올바른 방법 (0) | 2022.11.28 |
---|---|
MySQL JOIN과 LEFT JOIN의 차이점 (0) | 2022.11.28 |
MariaDB 10.5.8에서 Django 이행 실행 시 문제 (0) | 2022.11.28 |
스레드 사용 시 예외가 발생합니다.sleep(x) 또는 wait() (0) | 2022.11.28 |
스탠드아론 mariaDB 서버에 비해 galera의 퍼포먼스가 매우 나쁘다 (0) | 2022.11.28 |