오늘의하루

Java Stream이란? feat. 메모리 로드, 병렬 처리, 연산 흐름 본문

JAVA

Java Stream이란? feat. 메모리 로드, 병렬 처리, 연산 흐름

오늘의하루_master 2024. 5. 23. 10:11

Stream의 특징

스트림(Stream)은 Java에서 일련의 데이터를 함수형 연산을 통해 효율적으로 처리할 수 있는 메커니즘입니다.

기본적으로 스트림은 지연 평가를 사용하여 연산을 효율적으로 처리합니다.

이를 통해 필요할 때만 연산을 실행하고 중간 연산 결과를 최적화할 수 있습니다.

병렬 처리를 원할 경우, parallel() or parallelStream()를 사용하여 스트림을 병렬 처리 모드로 전환할 수 있습니다.

  • 병렬 Stream이 모든 상황에서 성능 향상을 보장하지 않습니다.

이렇게 하면 멀티 코어 CPU를 활용하여 대량의 데이터를 빠르게 처리할 수 있고, 병렬 처리된 각 부분의 결과를 최종적으로 합치는 과정을 거칩니다.

스트림의 이러한 특성 덕분에 Java에서는 복잡한 데이터 처리 작업을 보다 간편하게 처리할 수 있으며, 성능도 향상할 수 있습니다.

병렬처리 시 주의 사항

  1. 병렬 처리 시 여러 스레드가 나누어져 연산을 진행하기 때문에 연산이 끝나는 순서를 알 수 없어서 순서 보장이 되지 않는다.
  2. 병렬 처리 시 여러 스레드가 동시에 접근하기 때문에 공유 자원에 대한 동기화 문제를 고려해야 한다.
  3. 데이터가 작고 간단한 연산에 사용할 경우 오히려 성능에 악영향을 끼친다.

Stream의 연산 과정

Stream의 처리 과정은 생성 > 가공(N) > 최종 연산의 구조로 이루어져 있습니다.

  1. 생성:
    • 생성은 데이터 집합을 Stream으로 변환하는 과정으로, 최초 1번 수행됩니다.
    • 이때 데이터는 메모리에 로드되지 않으며, 어떤 데이터가 메모리에 로드될지는 가공 단계에서 결정됩니다.
  2. 가공:
    • 가공은 Stream으로 변환된 데이터 집합을 원하는 형태로 가공하는 것이며, 가공의 입력값과 결괏값은 모두 Stream 타입이므로 메서드 체이닝이 가능해집니다.
    • 가공의 가장 중요한 특징은 지연 평가(Lazy Evaluation)입니다.
    • 실제 Stream 사용 시 생성 후 가공 연산이 진행되지 않고, 최종 연산이 호출된 후 가공 연산이 진행됨을 의미하며 이때 첫 번째 가공 연산에서 필요한 데이터만 메모리에 로드됩니다.
  3. 최종 연산:
    • 최종 연산은 가공이 끝난 후 결과물을 얻기 위해 최초 1번 수행됩니다.
    • 최종 연산이 수행되면 Stream이 닫히기 때문에 이후 또다시 가공이나 최종 연산을 수행할 수 없습니다.

가공 단계 최적화

  • Fiilter와 Map 사용 시 Filter 연산을 먼저 진행하게 되면 Map 연산에서 불필요한 연산을 피할 수 있습니다.

Stream 예시

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamEx {
	public static void main(String args[]) {
	
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		// 일반 Stream
		List<Integer> streamTest = numbers.stream().filter(ele -> ele % 2 == 0).map(ele -> ele * ele).collect(Collectors.toList());
        
		System.out.println(streamTest);
		
	}
}
  • StreamTest의 경우 병렬처리가 되어 있지 않은 Stream입니다.
    • 병렬 처리를 하려면 stream(). parallel()... 또는 parallelStream()... 이렇게 작성해야 합니다.
  • 지연 평가로 인해 최종 연산 collect() 호출 후 중간 연산이 실행됩니다.
  • Stream은 numbers의 요소를 하나씩 메모리에 로드하여 처리합니다.
    • 병렬 처리하는 경우 각 스레드마다 사용하는 요소들이 메모리에 로드됩니다.
    • 간단한 연산을 병렬로 처리 시 오버헤드로 인해 성능에 악영향을 미칠 수 있습니다.

 

Comments