Java Stream
Java Stream 특징
- Java 8부터 도입된 기능으로, 컬렉션, 배열, 파일 등의 데이터 요소를 처리하는 연산을 지원하는 함수형 Stream 입니다.
- 데이터 소스로부터 연속된 데이터 흐름을 제공하여 데이터를 처리합니다.
- 데이터를 효율적으로 처리할 수 있으며, 병렬 처리를 포함한 다양한 연산을 적용할 수 있습니다.
- 데이터를 필터링, 매핑, 정렬, 그룹핑 등 다양한 작업을 수행할 수 있으며, 함수형 프로그래밍 스타일을 지원하여 코드를 간결하고 가독성 있게 작성할 수 있습니다.
- 중간 연산과 최종 연산으로 구성된 파이프라인을 만들 수 있습니다. 중간 연산은 다른 Stream 을 반환하며, 최종 연산은 최종 결과를 반환합니다.
Java Stream 처리 단계
1. 데이터 소스 생성
- 컬렉션, 배열, 파일 등의 데이터 소스로부터 Stream 을 생성합니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
2. 중간 연산
- 필터링, 매핑, 정렬 등의 작업을 수행할 수 있습니다.
stream = stream.filter(n -> n % 2 == 0) // 짝수만 필터링
.map(n -> n * 2) // 각 요소를 2배로 매핑
.sorted(); // 정렬
3. 최종 연산
- 리스트, 배열, 요소 개수 등 다양한 결과를 얻을 수 있습니다.
// 요소 개수 세기(count)
long count = numbers.stream()
.count();
System.out.println(count); // 출력: 5
// 요소들의 합 구하기(sum)
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
System.out.println(sum); // 출력: 15
// 최대값 찾기(max)
Optional<Integer> max = numbers.stream()
.max(Comparator.naturalOrder());
System.out.println(max.orElse(0)); // 출력: 5
// 요소들의 평균 구하기(average)
OptionalDouble average = numbers.stream()
.mapToInt(Integer::intValue)
.average();
System.out.println(average.orElse(0)); // 출력: 3.0
// 요소들을 리스트로 변환(collect)
List<Integer> doubledNumbers = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(doubledNumbers); // 출력: [2, 4, 6, 8, 10]
※ 이외에도 최소값 찾기(min), 문자열로 연결하기(join), 그룹화하기(groupingBy) 등 다양한 최종 연산이 있습니다.
Java Stream VS Parallel Stream
구분 | Stream | Parallel Stream |
데이터 처리 방식 | 순차적으로 연산을 수행 | 병렬로 연산을 수행 |
처리 속도 | 단일 CPU 코어에서 동작하며, 작업을 직렬로 처리. | 멀티코어 시스템에서 데이터 처리 속도를 높일 수 있음. 작업을 분할하고 스레드 간의 동기화 오버헤드가 발생할 수 있으므로, 작업량이 많거나 데이터가 충분히 큰 경우에 이점. |
스레드 안전성 | - | 멀티 스레드에서 작업을 수행하므로 스레드 안전성에 주의. thread-safe 한 자료구조를 사용하거나 동기화 메커니즘을 적절하게 사용 |
순서 보장 | 연산 순서가 보장되며, 요소 순서 유지 | 병렬로 처리하기 때문에 요소의 순서가 보장되지 않을 수 있음. |
사용처 | 데이터의 순서가 중요하거나 상호작용이 필요한 경우 유용 | 멀티코어 CPU 시스템에서 병렬 처리에 특히 유용하며, 대량의 데이터를 동시에 처리해야 할 때 성능 향상을 제공 |
소스로 비교
public static void main(String[] args) {
// 데이터 소스
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// stream
int sumSequential = Arrays.stream(numbers).sum();
System.out.println("Sum (Sequential): " + sumSequential);
// parallel stream
int sumParallel = Arrays.stream(numbers)
.parallel()
.sum();
System.out.println("Sum (Parallel): " + sumParallel);
}
// 결과 출력
Sum (Sequential): 55
Sum (Parallel): 55
stream : Arrays.stream(numbers)를 호출하여 스트림을 생성하고, sum() 메서드를 호출하여 모든 요소의 합을 계산
parallel stream : Arrays.stream(numbers)를 호출하여 스트림을 생성한 후 .parallel() 메서드를 호출하여 병렬 스트림으로 전환, 그리고 나서 sum() 메서드를 호출하여 병렬로 모든 요소의 합을 계산
※ 합계를 구하는 간단한 작업. 작은 데이터셋이므로 parallel stream 을 사용하여도 순차 stream 과 동일한 결과.
parallel stream 잘못된 사용 예제
public static void main(String[] args) {
// 데이터 소스
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 잘못된 사용 예제
int sum = Arrays.stream(numbers)
.parallel()
.map(n -> {
if (n == 5) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return n;
})
.sum();
System.out.println("Sum: " + sum);
}
// 결과 출력
Sum: 50
- 배열 numbers를 데이터 소스로 사용하여 parallel stream을 이용하여 요소들을 처리하고 합계(sum)를 계산
- parallel stream 내부의 map 연산에서 특정 조건(n == 5)에 따라 5인 경우 1초의 지연을 발생
- parallel stream은 요소를 병렬로 처리하기 때문에 map 연산의 각 요소는 병렬로 실행
- 조건 n == 5를 만족하는 요소가 존재할 경우 1초의 지연이 발생하고, 이로 인해 전체 연산에 대한 지연이 발생
parallel stream 처리 순서 확인
-public static void main(String[] args) {
// 데이터 소스
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Arrays.stream(numbers)
.parallel()
.map(n -> {
System.out.println("Map: " + n + " - " + Thread.currentThread().getName());
return n;
})
.forEach(n -> System.out.println("ForEach: " + n + " - " + Thread.currentThread().getName()));
-}
// 결과 출력
Map: 1 - main
Map: 2 - ForkJoinPool.commonPool-worker-1
Map: 5 - ForkJoinPool.commonPool-worker-2
Map: 4 - ForkJoinPool.commonPool-worker-3
Map: 3 - ForkJoinPool.commonPool-worker-4
Map: 6 - main
ForEach: 6 - main
ForEach: 1 - main
ForEach: 2 - ForkJoinPool.commonPool-worker-1
ForEach: 3 - ForkJoinPool.commonPool-worker-4
ForEach: 4 - ForkJoinPool.commonPool-worker-3
ForEach: 5 - ForkJoinPool.commonPool-worker-2
- 배열 numbers를 데이터 소스로 사용하여 parallel stream을 생성하고, map 연산과 forEach 연산을 순차적으로 수행.
- map 연산은 각 요소를 변환하고, forEach 연산은 각 요소를 출력.
- Thread.currentThread().getName() : 현재 실행 중인 스레드의 이름 출력.
- map 연산은 여러 스레드에서 동시에 실행되므로 요소의 처리 순서가 보장되지 않음.
- forEach 연산은 마지막에 순차적으로 수행되며, 최종 결과인 출력도 순차적으로 출력
※ 중간 연산과 최종 연산의 특성에 따라 다를 수 있으므로, 순서에 의존하는 작업을 수행하는 경우에는 주의가 필요
Java 8 에 추가된 기능인 Stream 에 대해서 간략하게 알아보았습니다. 더불어 parallel stream 과의 비교도 확인하였습니다.
'프로그래밍 > Java' 카테고리의 다른 글
java 소수점 처리 (1) | 2023.10.10 |
---|---|
[java] 엑셀 읽기 에러 Exception in thread "main" java.lang.NoSuchMethodError: org.apache.logging.log4j.Logger.atDebug()Lorg/apache/logging/log4j/LogBuilder; (0) | 2023.06.15 |
Stream reduce 간단 설명 (0) | 2023.05.20 |
java file.encoding (0) | 2022.07.12 |
[JAVA] java.lang.UnsatisfiedLinkError: no net in java.library.path (1) | 2021.06.25 |