JAVA

JAVA - 스트림(Stream)

윤승 2025. 3. 13. 20:48

✏️ 자바 스트림(Stream)이란?

스트림(Stream)은 데이터를 효율적으로 처리할 수 있는 흐름이며, 기존의 for 문이나 Iterator보다 가독성과 재사용성이 뛰어난 선언형 스타일로 작성할 수 있다.

 


 

✏️ 스트림의 특징

  1. 선언형 스타일
    • for 문이나 Iterator를 사용하지 않고, 데이터를 변환하고 필터링하는 작업을 간결하게 표현할 수 있다.
  2. 일관된 방식으로 데이터 처리 가능
    • List, Set, Map, 배열 등 다양한 데이터 소스를 같은 방식으로 다룰 수 있다.
  3. 데이터 흐름 방식
    • 데이터 준비 → 중간 연산 → 최종 연산 순으로 처리된다.

 

✏️ for 문 vs 스트림 비교

각 요소를 10배로 변환 후 출력하는 예시로 알아보자

  • arrayList 의 각 요소를 10배로 변환한다.
  • 아래 예시를 보고 for 문과 스트림 을 비교해보자

▼ for 문 (명령형 스타일)

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // for 문을 사용한 방식
        List<Integer> ret1 = new ArrayList<>();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10; // 각 요소 * 10
            ret1.add(multipliedNum);
        }
        System.out.println("ret1 = " + ret1);  // [10, 20, 30, 40, 50]
    }
}

 

특징:

- 명령형(Imperative) 스타일: 어떻게 동작하는지를 한 단계씩 설명해야 함.

- 가독성↓: for 문을 사용하면 코드 길이가 길어짐.

- 명확한 데이터 변환 과정: num * 10 후 ret1.add() 수행.

 

▼ 스트림 (선언형 스타일)

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

public class Main {
    public static void main(String[] args) {
        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // 스트림을 활용한 방식
        List<Integer> ret2 = arrayList.stream()
                                      .map(num -> num * 10)  // 각 요소 * 10
                                      .collect(Collectors.toList());

        System.out.println("ret2 = " + ret2);  // [10, 20, 30, 40, 50]
    }
}

 

특징:

- 선언형(Declarative) 스타일: 무엇을 할 것인지만 명확히 기술.

- 가독성↑: 한 줄로 데이터 변환을 표현할 수 있어 간결함.

- 병렬 처리 가능: .parallelStream()을 사용하면 멀티스레드로 처리 가능.

 

✏️ 스트림을 사용하여 각 요소를 10배로 변환하는 과정

스트림 처리 순서

1️⃣ stream() → 2️⃣ map() → 3️⃣ collect() 순으로 데이터 흐름을 처리합니다.

 

1. stream(): 데이터 준비

  • 컬렉션(List, Set 등)을 스트림 형태로 변환하여 데이터를 처리할 준비를 합니다.
  • 스트림은 요소를 하나씩 처리하며, 다양한 연산을 수행할 수 있습니다.
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
Stream<Integer> stream = arrayList.stream();

 

2. map(): 중간 연산 (데이터 변환)

  • map()은 각 요소를 주어진 함수에 적용하여 새로운 값을 생성합니다.
  • 여기서는 각 요소를 10배로 변환하는 연산을 수행합니다.
Stream<Integer> mappedStream = arrayList.stream().map(num -> num * 10);

 

3. collect(): 최종 연산 (결과 수집)

  • collect()는 스트림에서 처리된 데이터를 List, Set 등 원하는 형태로 변환합니다.
  • 최종 연산이 실행되면 스트림은 더 이상 사용할 수 없습니다.
List<Integer> result = arrayList.stream()
                                .map(num -> num * 10) 
                                .collect(Collectors.toList());
System.out.println(result);  // [10, 20, 30, 40, 50]

전체 코드 예제

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

public class Main {
    public static void main(String[] args) {
        // ✅ 1. 데이터 준비
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ 2. 중간 연산 (각 요소를 10배로 변환)
        // ✅ 3. 최종 연산 (List로 수집)
        List<Integer> result = arrayList.stream()
                                        .map(num -> num * 10)
                                        .collect(Collectors.toList());

        System.out.println(result);  // [10, 20, 30, 40, 50]
    }
}

✏️ 스트림의 장점

✔️ 반복문 없이 간결한 코드
✔️ 가독성이 높아 유지보수가 쉬움
✔️ 데이터 소스에 관계없이 동일한 방식으로 처리 가능
✔️ 병렬 처리를 쉽게 구현 가능 (parallelStream())