stream() - 개념 이해 / 정리

StrayCat·2026년 2월 20일

CS지식

목록 보기
3/32

stream() 개념을 확실히 하고 용례를 정리한다


.stream() VS Arrays.stream(arr)

방식대상예시
.stream()컬렉션 (List,Set,Map 등)list.stream()
Arrays.stream()배열(int[], String[] 등)Arrays.stream(arr)

1. .stream() - 컬렉션에서 사용

List<Integer> list = List.of(1, 2, 3, 4, 5);

int sum = list.stream()
              .filter(n -> n % 2 == 0)
              .mapToInt(Integer::intValue)
              .sum();
  • Collection 인터페이스에 .stream() 메서드가 정의되어 있기 때문에, List/Set 등 컬렉션 계열은 .stream()을 통해 바로 호출 가능

2. Arrays.stream() - 배열에서 사용

int[] arr = {1, 2, 3, 4, 5};

int sum = Arrays.stream(arr)
                .filter(n -> n % 2 == 0)
                .sum();
  • 배열은 Collection이 아니므로 .stream() 보유하지 않음. 대신 Arrays 유틸 클래스의 정적 메서드를 사용함
  • ex) int[]Arrays.stream()을 쓰면 자동으로 IntStream이 반환되어 .sum(), .average() 등을 바로 쓸 수 있어서 편리합니다.

Collection이란?

Collection 인터페이스란 Java에서 "여러 객체를 담는 자료구조들의 공통 규격"

계층 구조로 보기

Iterable
  └── Collection        ← 여기
        ├── List        (순서 O, 중복 O)
        │     ├── ArrayList
        │     └── LinkedList
        ├── Set         (순서 X, 중복 X)
        │     ├── HashSet
        │     └── TreeSet
        └── Queue
              └── LinkedList
  • Map의 경우 Collection을 상속하지 않는 별개의 인터페이스
  • Collection 인터페이스에는 .stream() 메서드가 정의되어 있음
  • 이를 구현한 구현체인 ArrayList, HashSet 등 모든 컬렉션에서 호출 가능
// 모두 Collection을 구현 → .stream() 사용 가능
new ArrayList<>().stream();
new HashSet<>().stream();
new LinkedList<>().stream();

자주 헷갈리는 패턴

1. .stream() / Arrays.stream() 사용 시기

// ❌ 배열에 .stream() 직접 호출 → 불가
int[] arr = {1, 2, 3};
arr.stream(); // 컴파일 에러!

// ✅ 배열 → Stream으로 변환하려면
Arrays.stream(arr)  // int[] → IntStream

// String[] 도 마찬가지
String[] strArr = {"a", "b", "c"};
Arrays.stream(strArr).forEach(System.out::println);

2. 중간 연산 vs 최종 연산

list.stream()
    .filter(n -> n > 3)    // 중간 연산 - 아직 실행 안 됨!
    .map(n -> n * 2)       // 중간 연산 - 아직 실행 안 됨!
    .collect(...)          // 최종 연산 - 여기서 한 번에 실행됨
  • 중간 연산은 lazy(지연)합니다. 최종 연산이 호출되기 전까지 실제로 아무것도 실행되지 않습니다.
  • 최종 연산 종류 : collect(), forEach(), count(), sum(), findFirst(), anyMatch()

3. Stream은 1회용이다

Stream<Integer> stream = list.stream();

stream.filter(n -> n > 3).collect(toList()); // ✅ 정상
stream.map(n -> n * 2).collect(toList());    // ❌ 에러! 이미 소비된 스트림
  • 한 번 최종 연산(위에서는 collect)을 호출하면 Stream은 닫힙니다. .stream()을 통해 새로 호출해야 합니다.

4. map vs flatMap

List<List<Integer>> nested = List.of(
    List.of(1, 2),
    List.of(3, 4)
);

// map → Stream<List<Integer>> (리스트가 그대로 원소)
nested.stream()
      .map(inner -> inner)  // [[1,2], [3,4]]

// flatMap → Stream<Integer> (펼쳐서 하나의 스트림으로)
nested.stream()
      .flatMap(inner -> inner.stream())  // [1, 2, 3, 4]
  • map은 1:1 변환, flatMap은 중첩 구조를 펼칠 때 사용합니다.
  • 2차원 배열이나 리스트 안의 리스트를 다룰 때 자주 사용합니다.

5. IntStream vs Stream<Integer>

int[] arr = {1, 2, 3};

// Arrays.stream(int[]) → IntStream (기본형 특화 스트림)
Arrays.stream(arr).sum();      // ✅ 바로 사용 가능
Arrays.stream(arr).average();  // ✅

// Stream<Integer>는 sum() 없음
list.stream().sum(); // ❌ 컴파일 에러

// Stream<Integer> → IntStream 변환
list.stream()
    .mapToInt(Integer::intValue)  // 여기서 IntStream으로 변환
    .sum(); // ✅

IntStream, LongStream, DoubleStream은 기본형에 특화된 스트림으로, .sum(), .average(), .min(), .max() 등을 바로 쓸 수 있습니다.

6. collect() - 가장 많이 쓰는 최종 연산

// List로 수집
List<Integer> result = stream.collect(Collectors.toList());

// Map으로 수집 (코테에서 자주 등장)
Map<String, Integer> map = stream.collect(
    Collectors.toMap(
        s -> s,           // key
        s -> s.length()   // value
    )
);

// 그룹핑 (빈도수 문제 등에서 유용)
Map<Integer, List<String>> grouped = stream.collect(
    Collectors.groupingBy(String::length)
);

// 빈도수 카운팅
Map<String, Long> freq = stream.collect(
    Collectors.groupingBy(s -> s, Collectors.counting())
);

7. sorted() 정렬 방향

list.stream().sorted();                          // 오름차순 (기본)
list.stream().sorted(Comparator.reverseOrder()); // 내림차순

// 객체 정렬
list.stream().sorted(Comparator.comparing(Person::getAge));             // 나이 오름차순
list.stream().sorted(Comparator.comparing(Person::getAge).reversed());  // 나이 내림차순

// 다중 조건 정렬
list.stream().sorted(
    Comparator.comparing(Person::getAge)
              .thenComparing(Person::getName)  // 나이 같으면 이름순
);
profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글