본문 바로가기
쿤즈 Dev/Kotlin

[자바 스트림 1] 스트림 생성 5가지 방법 — 면접 단골 기본기

by Koonz:) 2026. 10. 17.
728x90
요약: 스트림으로 데이터를 다루려면 먼저 스트림을 "생성" 해야 합니다. 생성 방법은 데이터가 어디서 오느냐에 따라 크게 5가지 — ① 컬렉션 stream(), ② 배열 Arrays.stream(), ③ 낱개 값 Stream.of(), ④ 규칙 반복 Stream.iterate(), ⑤ 공급 함수 Stream.generate() 입니다. 이 글에서 각각의 코드와 차이, 그리고 면접에서 반드시 묻는 "무한 스트림엔 limit가 필수" 함정까지 정리합니다.


코딩 테스트나 면접에서 스트림 문제를 받으면, 가장 먼저 해야 할 일은 "이 데이터로 스트림을 어떻게 만들까?" 입니다. 리스트가 주어졌는지, 배열인지, 아니면 "1부터 10까지" 같은 규칙만 주어졌는지에 따라 생성 방법이 달라지기 때문이죠. 생성만 제대로 골라도 뒤의 map·filter·collect는 자연스럽게 이어집니다.

 

면접에서도 "스트림을 만드는 방법을 아는 대로 말해보라" 는 질문이 기본기 확인용으로 자주 나옵니다. 5가지를 코드와 함께 확실히 정리해 두겠습니다.

 

자바 스트림 생성 5가지 방법 stream Arrays of iterate generate

 

문제 정의

 

문제.
아래 4가지 데이터로부터 각각 스트림을 만들어 출력하라.
1. List<String> names = ["Kim", "Lee", "Park"]
2. int[] scores = {90, 85, 70}
3. 낱개 값 "A", "B", "C"
4. "2부터 2씩 커지는 짝수 5개"(2, 4, 6, 8, 10)

 

  • 입력: 위 네 가지 데이터 소스
  • 기대 출력:
  • Kim Lee Park
  • 90 85 70
  • A B C
  • 2 4 6 8 10

 

전통 방식(for-loop) — 그리고 스트림이 나은 이유

 

스트림이 없다면 각 데이터를 for 루프로 돌려야 합니다. 특히 "짝수 5개 생성"처럼 규칙으로 값을 만들어내는 경우 코드가 장황해집니다.

 

// 전통 방식 — 소스마다 루프를 따로 짜야 한다
import java.util.List;

public class Traditional {
    public static void main(String[] args) {
        // 1. 리스트
        List<String> names = List.of("Kim", "Lee", "Park");
        for (String name : names) {
            System.out.print(name + " ");
        }
        System.out.println();

        // 4. 짝수 5개 — 규칙 생성은 인덱스/변수 관리가 번거롭다
        int value = 2;
        for (int i = 0; i < 5; i++) {
            System.out.print(value + " ");
            value += 2;
        }
        System.out.println();
    }
}

 

for 루프도 동작은 합니다. 하지만 "무엇을 하는지(의도)"가 루프 제어 변수(i, value) 뒤에 가려집니다. 스트림은 "소스 → 생성 → 가공 → 수집"을 선언형으로 표현해, 뒤에 map·filter를 붙여 나가기에 훨씬 깔끔합니다.

 

스트림 해법 — 생성 5가지

 

데이터 소스에서 Stream 생성 5경로 방사형 iterate generate limit

 

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.IntStream;

public class StreamCreate {
    public static void main(String[] args) {
        // ① 컬렉션에서 — 가장 흔한 방법
        List<String> names = List.of("Kim", "Lee", "Park");
        names.stream()
             .forEach(n -> System.out.print(n + " "));
        System.out.println();

        // ② 배열에서 — Arrays.stream
        int[] scores = {90, 85, 70};
        Arrays.stream(scores)
              .forEach(s -> System.out.print(s + " "));
        System.out.println();

        // ③ 낱개 값에서 — Stream.of
        Stream.of("A", "B", "C")
              .forEach(v -> System.out.print(v + " "));
        System.out.println();

        // ④ 규칙 반복 — Stream.iterate (반드시 limit로 끊는다!)
        Stream.iterate(2, n -> n + 2)   // 2, 4, 6, 8, ...
              .limit(5)
              .forEach(n -> System.out.print(n + " "));
        System.out.println();

        // ⑤ 공급 함수 — Stream.generate (역시 limit 필수)
        Stream.generate(() -> 0)        // 0, 0, 0, ...
              .limit(3)
              .forEach(n -> System.out.print(n + " "));
        System.out.println();

        // (보너스) 숫자 범위는 IntStream.rangeClosed가 더 직관적
        IntStream.rangeClosed(1, 5)     // 1, 2, 3, 4, 5
                 .forEach(n -> System.out.print(n + " "));
        System.out.println();
    }
}

 

한 줄씩 설명

 

  • ① names.stream() — 모든 Collection(List·Set 등)은 .stream() 메서드를 가집니다. 실무에서 가장 많이 쓰는 생성법입니다.
  • ② Arrays.stream(scores) — 배열은 .stream()이 없으므로 Arrays.stream()에 넘깁니다. int[]를 넘기면 기본형 특화 스트림인 IntStream이 반환돼 sum()·average() 같은 숫자 집계를 바로 쓸 수 있습니다.
  • ③ Stream.of("A", "B", "C") — 낱개 값들을 직접 나열해 스트림을 만듭니다. 가변인자(varargs)라 값 개수는 자유입니다.
  • ④ Stream.iterate(2, n -> n + 2) — 시드(2) 에서 시작해 다음 값 규칙(n -> n + 2) 을 반복 적용합니다. 끝이 없으므로 .limit(5)로 끊어야 합니다.
  • ⑤ Stream.generate(() -> 0) — 인자 없는 공급 함수(Supplier)로 값을 계속 만들어냅니다. 난수(Math::random)처럼 "이전 값과 무관한" 생성에 적합하며, 이것도 .limit가 필수입니다.
  • (보너스) IntStream.rangeClosed(1, 5) — "1부터 5까지" 같은 연속 정수 범위는 iterate보다 range/rangeClosed가 더 명확합니다. range는 끝 제외, rangeClosed는 끝 포함입니다.

 

실행 결과

 

Kim Lee Park 
90 85 70 
A B C 
2 4 6 8 10 
0 0 0 
1 2 3 4 5 

 

변형·함정·주의

 

  • ⚠ 무한 스트림엔 limit가 필수. Stream.iterate와 Stream.generate는 끝이 없는 스트림을 만듭니다. limit 없이 forEach나 collect를 호출하면 프로그램이 무한 루프에 빠집니다. 면접에서 가장 자주 지적되는 포인트입니다.
  • 종료 조건이 있는 iterate(Java 9+). 3개 인자 버전 Stream.iterate(1, n -> n <= 10, n -> n + 1) 은 가운데 조건이 거짓이 되면 스스로 멈춥니다(limit 불필요). for 루프와 가장 닮은 형태입니다.
  • 스트림은 일회용. 한 번 소비(최종 연산 실행)한 스트림은 재사용할 수 없습니다. 다시 쓰면 IllegalStateException: stream has already been operated upon or closed 가 납니다. 같은 데이터를 두 번 처리하려면 스트림을 두 번 생성하세요.
  • 기본형 특화 스트림을 활용하라. 숫자는 Stream<Integer>보다 IntStream/LongStream/DoubleStream이 박싱 오버헤드가 없고 sum()·average()·max() 같은 집계 메서드를 바로 제공합니다.
  • 빈 스트림. 요소가 없을 수 있으면 Stream.empty() 로 빈 스트림을 안전하게 만들 수 있습니다(null 대신).

 

관련 면접 질문

 

  • Q1. 스트림을 생성하는 방법을 아는 대로 설명해 주세요.

 

→ 컬렉션 stream(), 배열 Arrays.stream(), 낱개 값 Stream.of(), 규칙 반복 Stream.iterate(), 공급 함수 Stream.generate() 5가지. 숫자 범위는 IntStream.range(Closed)를 추가로 언급하면 좋습니다.

 

  • Q2. Stream.iterate와 Stream.generate를 쓸 때 반드시 주의할 점은?

 

→ 둘 다 무한 스트림이므로 limit(또는 iterate의 종료 조건 오버로드)으로 반드시 크기를 제한해야 합니다. 안 그러면 무한 루프에 빠집니다.

 

시리즈 바로가기

 

 

스트림을 만들었으니, 다음 편에서는 그 요소들을 다른 값으로 바꾸는 map 을 다룹니다. 스트림 가공의 출발점이니 꼭 이어서 보세요.

댓글