티스토리 뷰

🍀 Spring Boot

forEach vs stream

James Wetzel 2025. 2. 18. 14:51
728x90
반응형

forEach와 stream() 메서드는 둘 다 컬렉션의 요소를 반복(iterate)하는 기능을 제공하지만, 동작 방식과 목적이 다릅니다.


✅ forEach vs. stream() 차이점 요약

구분 forEach stream()

목적 단순 반복(iteration) 수행 데이터 변환 및 가공
반환값 없음 (void) 변환된 스트림을 반환
병렬 처리 parallelStream().forEach() 사용 가능하지만, 비순차적 실행 가능성 있음 parallelStream()을 활용하면 병렬 처리가 안정적으로 가능
사용 예 로그 출력, 데이터 출력 등 필터링, 변환, 수집 등 데이터 가공
원본 컬렉션 변경 여부 원본 컬렉션 수정 가능 원본 컬렉션을 변경하지 않음 (불변성 유지)

1️⃣ forEach : 단순 반복(iteration)

  • forEach는 스트림이 아닌 컬렉션(List, Set, Map 등)에 직접 사용 가능합니다.
  • 주로 컬렉션의 요소를 하나씩 순회하면서 출력하거나 상태를 변경할 때 사용합니다.

✅ forEach 예제 (출력용)

List<String> names = List.of("Alice", "Bob", "Charlie");

names.forEach(name -> System.out.println(name));

출력

Alice
Bob
Charlie
  • 단순한 반복(iteration) 기능만 수행합니다.
  • 반환값이 없고(void), 연산 결과를 저장할 수도 없습니다.

2️⃣ stream()

  • 함수형 프로그래밍 스타일을 제공하며, 데이터 변환과 가공이 가능하도록 설계되었습니다.
  • 원본 컬렉션을 변경하지 않으며, map(), filter(), collect() 같은 연산을 통해 변형된 데이터를 만들 수 있습니다.

✅ stream() 예제 (대문자로 변환 후 리스트 수집)

List<String> names = List.of("Alice", "Bob", "Charlie");

List<String> upperCaseNames = names.stream()
        .map(String::toUpperCase)  // 모든 이름을 대문자로 변환
        .collect(Collectors.toList());  // 리스트로 수집

System.out.println(upperCaseNames);

출력

[ALICE, BOB, CHARLIE]
  • 원본 리스트는 변경되지 않으며, 새로운 리스트를 생성합니다.
  • map(String::toUpperCase)를 사용해 데이터 변환이 가능합니다.

3️⃣ forEach vs. stream() 코드 비교

🚀 forEach (데이터를 변환하려고 할 때 문제 발생)

List<String> names = List.of("Alice", "Bob", "Charlie");

List<String> upperCaseNames = new ArrayList<>();
names.forEach(name -> upperCaseNames.add(name.toUpperCase()));

System.out.println(upperCaseNames);
  • upperCaseNames라는 새로운 리스트를 만들고, forEach 안에서 값을 추가해야 합니다.
  • 코드가 명확하지 않고, 병렬 처리를 지원하지 않습니다.

✅ stream() (데이터 변환을 더 간결하게)

List<String> upperCaseNames = names.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());

System.out.println(upperCaseNames);
  • 코드가 더 간결하고 효율적입니다.
  • 병렬 처리를 원하면 .parallelStream()을 사용할 수 있습니다.

4️⃣ 병렬 처리 (Parallel Processing) 차이점

🚨 forEach 병렬 처리 문제점

List<Integer> numbers = IntStream.rangeClosed(1, 10)
        .boxed()
        .collect(Collectors.toList());

numbers.parallelStream().forEach(n -> System.out.print(n + " "));

출력 (실행할 때마다 순서가 달라짐)

3 1 4 2 5 6 7 9 8 10 
  • parallelStream().forEach()를 사용하면 순서 보장이 어렵습니다.

✅ stream()을 사용한 병렬 처리 (순서 보장)

numbers.parallelStream()
       .forEachOrdered(n -> System.out.print(n + " "));

출력 (순서 보장)

1 2 3 4 5 6 7 8 9 10
  • forEachOrdered()를 사용하면 병렬 처리 시에도 순서를 유지할 수 있습니다.

✅ 결론 (언제 forEach vs. stream()을 사용할까?)

상황 forEach 사용 stream() 사용

단순 반복
데이터 변환
요소 필터링
결과를 새로운 리스트/셋으로 만들 때
병렬 처리 ❌ (parallelStream().forEach()는 순서 문제 발생 가능) ✅ (forEachOrdered() 사용 가능)

정리하면

  • 단순 반복(iteration)만 필요하면 forEach
  • 데이터 변환 및 가공이 필요하면 stream()
  • 병렬 처리가 필요하면 stream().parallelStream()
  • 원본 컬렉션을 변경하려면 forEach, 불변성을 유지하려면 stream()

🚀 마무리

  • forEach는 단순한 반복(iteration) 용도로 사용됩니다.
  • stream()은 변환(map), 필터링(filter), 수집(collect) 등 다양한 데이터 가공 기능을 제공합니다.
  • 데이터를 변환해야 한다면 stream()이 더 적절합니다.
  • 병렬 처리가 필요할 경우 parallelStream()을 사용할 수 있지만, 순서가 중요하면 forEachOrdered()를 활용해야 합니다.

 

728x90
반응형