Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags more
Archives
Today
Total
관리 메뉴

프로그래밍 공부

[자바] #22. 스트림(Stream) - 3편 (최종 연산 추가 내용) 본문

자바

[자바] #22. 스트림(Stream) - 3편 (최종 연산 추가 내용)

하 냥 2025. 2. 21. 17:01

📚 최종 연산

최종 연산은 스트림 처리의 마지막 단계에서 결과를 반환하거나 처리하는 연산.
최종 연산이 호출될 때 중간 연산이 실제로 실행된다.


1️⃣ sum()

스트림 요소의 합계 반환.

📈 적용 대상: IntStream, LongStream, DoubleStream

int sum = IntStream.of(1, 2, 3, 4, 5).sum();  // 출력: 15

2️⃣ count()

스트림의 요소 개수 반환. (long 타입으로 반환)

long count = Stream.of("a", "b", "c").count();  // 출력: 3

3️⃣ average()

스트림 요소의 평균값 반환. (OptionalDouble 타입으로 반환)

📈 적용 대상: IntStream, LongStream, DoubleStream

OptionalDouble avg = IntStream.of(1, 2, 3, 4, 5).average();
avg.ifPresent(System.out::println);  // 출력: 3.0
// OptionalDouble 타입으로 반환되므로 ifPresent 사용 가능

4️⃣ min()

스트림 요소 중 최솟값 반환. (OptionalInt 또는 OptionalDouble 타입으로 반환)

OptionalInt min = IntStream.of(5, 2, 9).min();
min.ifPresent(System.out::println);  // 출력: 2

5️⃣ max()

스트림 요소 중 최댓값 반환. (OptionalInt 또는 OptionalDouble 타입으로 반환)

OptionalInt max = IntStream.of(5, 2, 9).max();
max.ifPresent(System.out::println);  // 출력: 9

6️⃣ forEach()

스트림의 각 요소에 대해 작업 수행. (순차적으로 처리)

Stream.of("Java", "Python", "C++").forEach(System.out::println);

7️⃣ allMatch()

모든 요소가 조건을 만족하는지 여부를 반환. (boolean 타입으로 반환)

boolean allEven = IntStream.of(2, 4, 6).allMatch(n -> n % 2 == 0);  // 출력: true

8️⃣ anyMatch()

하나 이상의 요소가 조건을 만족하는지 여부를 반환. (boolean 타입으로 반환)

boolean hasNegative = Stream.of(1, -2, 3).anyMatch(n -> n < 0);  // 출력: true

9️⃣ noneMatch()

모든 요소가 조건을 만족하지 않는지 여부를 반환. (boolean 타입으로 반환)

boolean noneEmpty = Stream.of("A", "B", "").noneMatch(String::isEmpty);  // 출력: false

🔟 collect()

스트림의 요소를 컬렉션(List, Set, Map 등)으로 수집하거나 요약

📈 주요 메서드: Collectors.toList(), Collectors.toSet(), Collectors.joining() 등

List<String> list = Stream.of("Java", "Python").collect(Collectors.toList());
System.out.println(list);  // 출력: [Java, Python]

 

Stream<String>의 요소들을 List<String>로 수집.

Collectors.toList()스트림을 리스트로 변환하는 역할을 한다.

Collectors.toSet()은 List<Set>으로 변한, Collectors.toMap()은 List<Map>으로 변환...

 

또, Collectors.joining(" ")은

스트림에 있는 문자열들을 공백 " "을 구분자로 사용하여 연결해서 하나의 문자열로 결합한다.

String sentence = Stream.of("Java", "is", "awesome")
                        .collect(Collectors.joining(" "));  // 문자열 연결

System.out.println(sentence);  // 출력: Java is awesome

🚀 왜 collect() 메서드를 사용해야 할까?

collect()가 필요한 이유:

  • 스트림은 일회성이다.
    • 스트림을 사용하여 데이터를 처리하고 나면 재사용할 수 없다.
  • 스트림 결과를 컬렉션에 저장하거나 원하는 형태로 반환하기 위해 collect()가 필요

 

 

🌟 매개변수를 3개 받는 collect 메서드

<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);

 

✅ 각 매개변수의 역할:

매개변수 타입 역할
supplier Supplier<R> 결과 컨테이너 생성 (예: ArrayList::new)
accumulator BiConsumer<R, T> 각 스트림 요소를 결과 컨테이너에 누적하는 로직
combiner BiConsumer<R, R> 병렬 처리 시 부분 결과 병합하는 로직 (parallelStream 사용 시 중요)

 

  • collect() 메서드의 3개 매개변수 버전은 커스텀 수집 로직을 구현할 수 있게 해준다.
  • Collectors.toList() 같은 기본 수집기 대신, 사용자 정의 수집 방식을 직접 정의할 수 있다.
  • 병렬 스트림(parallelStream()) 사용 시 부분 결과를 결합하는 combiner가 필요.

🚀 예제: collect()를 사용하여 List 수집

import java.util.*;
import java.util.stream.*;

public class CustomCollectExample {
    public static void main(String[] args) {
        List<String> names = Stream.of("Alice", "Bob", "Charlie")
                                   .collect(
                                       ArrayList::new, // supplier: 새로운 ArrayList 생성
                                       List::add,      // accumulator: 각 요소를 리스트에 추가
                                       List::addAll    // combiner: 병렬 처리 시 부분 리스트 병합
                                   );
        System.out.println(names);  // 출력: [Alice, Bob, Charlie]
    }
}

📝 과정 설명:

1️⃣ supplier:

  • ArrayList::new
  • 결과를 담을 빈 리스트를 생성

2️⃣ accumulator:

  • List::add
  • 각 스트림 요소(Alice, Bob, Charlie)를 리스트에 추가

3️⃣ combiner:

  • List::addAll
  • 병렬 처리 시 부분적으로 생성된 리스트들을 하나로 병합

하지만 이 예제에서는 순차 스트림을 사용했기 때문에 combiner인 List::addAll은 실제로 호출되지 않는다 !!

 

동일한 작업을 기본 수집기로 수행:

List<String> names = Stream.of("Alice", "Bob", "Charlie")
                           .collect(Collectors.toList());
System.out.println(names);  // 출력: [Alice, Bob, Charlie]

🎨 예제: 문자열을 연결하는 커스텀 collect()

스트림의 문자열을 쉼표로 연결하는 예제.

import java.util.stream.*;

public class StringJoinCustomCollect {
    public static void main(String[] args) {
        String result = Stream.of("Java", "Python", "C++")
                               .collect(
                                   StringBuilder::new,                  // supplier: StringBuilder 생성
                                   (sb, str) -> sb.append(str).append(","), // accumulator: 문자열과 쉼표 추가
                                   StringBuilder::append                 // combiner: 병렬 처리 시 StringBuilder 병합
                               )
                               .toString();
        // 마지막 쉼표 제거
        result = result.endsWith(",") ? result.substring(0, result.length() - 1) : result;
        System.out.println(result);  // 출력: Java,Python,C++
    }
}

📝 과정 설명:

1️⃣ supplier:

  • StringBuilder::new
  • 문자열을 효율적으로 연결할 수 있는 StringBuilder를 생성

2️⃣ accumulator:

  • (sb, str) -> sb.append(str).append(",")
  • 각 문자열을 StringBuilder에 추가하면서, 쉼표로 구분

3️⃣ combiner:

  • StringBuilder::append
  • 병렬 처리 시 부분 문자열을 결합

🏃 예제: 병렬 스트림(parallelStream())에서 combiner 확인

import java.util.*;
import java.util.stream.*;

public class ParallelCollectExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        String result = numbers.parallelStream()       // 병렬 스트림 사용
                                .collect(
                                    StringBuilder::new,
                                    (sb, num) -> sb.append(num).append(","),
                                    (sb1, sb2) -> sb1.append(sb2)   // 병합 과정
                                )
                                .toString();

        result = result.endsWith(",") ? result.substring(0, result.length() - 1) : result;
        System.out.println("결과: " + result);  // 출력: (순서가 바뀔 수 있음, 예: 3,1,5,2,4)
    }
}

⚡ 설명:

  • parallelStream()을 사용하면 스트림이 멀티스레드 환경에서 처리된다.
  • 각 스레드에서 부분적으로 StringBuilder를 만든 후, combiner가 이 부분 결과를 하나의 StringBuilder로 병합한다.
  • 이 과정에서 순서가 바뀔 수 있음에 주의.

📝 과정 설명:

 

  • 스트림의 각 요소(num)가 StringBuilder(sb)에 누적(append)된다.
  • sb.append(num)로 요소를 문자열로 추가하고, .append(",")로 각 요소 뒤에 쉼표(,)를 붙인다.
  • 병렬 스트림(parallelStream())은 작업을 여러 스레드에 분할하여 처리하는데,
  • 각 스레드는 독립적인 StringBuilder(부분 결과)를 생성하고,
  • combiner는 이러한 부분 결과(sb1, sb2)를 하나의 최종 결과로 병합한다.