복잡한뇌구조마냥

[JAVA] 스트림 ( Stream ) 본문

BE/JAVA

[JAVA] 스트림 ( Stream )

지금해냥 2025. 7. 28. 09:01

✅ 개요

Java 8부터 도입된 Stream데이터를 선언형 방식으로 처리할 수 있는 기능입니다.
기존의 for-loop 방식보다 간결하고 가독성이 높으며, 병렬 처리도 간편하게 할 수 있어 자바 개발자라면 꼭 익혀야 할 기능입니다.


🔍 1. Stream이란?

Stream은 데이터의 흐름(데이터 스트림)을 추상화한 개념입니다.
컬렉션(List, Set 등)을 반복하거나 필터링/변환할 때, 스트림 API를 사용하면 더 선언적이고 간결한 코드를 작성할 수 있습니다.

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

names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println); // 출력: Alice

🔧 2. Stream 사용 구조

collection.stream()
          .중간연산1()
          .중간연산2()
          ...
          .최종연산();
  • 중간 연산: filter(), map(), sorted() 등 → 스트림을 변환 (lazy 연산)
  • 최종 연산: forEach(), collect(), count(), reduce() 등 → 결과를 반환

📌 Stream은 lazy하기 때문에 최종 연산을 호출해야 중간 연산이 실행됩니다.


🛠️ 3. 주요 연산 예시

🔸 filter(): 조건에 맞는 요소만 필터링

List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream().filter(n -> n % 2 == 0).forEach(System.out::println); // 2, 4

🔸 map(): 요소 변환

List<String> names = List.of("kim", "lee", "park");
names.stream().map(String::toUpperCase).forEach(System.out::println);

🔸 sorted(): 정렬

List<String> names = List.of("banana", "apple", "cherry");
names.stream().sorted().forEach(System.out::println);

🔸 collect(): 결과 수집 (List 등으로)

List<String> filtered = names.stream()
                             .filter(name -> name.length() <= 3)
                             .collect(Collectors.toList());

🔸 reduce(): 누적 계산

int sum = List.of(1, 2, 3, 4).stream()
                             .reduce(0, Integer::sum); // 10​

⚙️ 4. 병렬 처리 (parallelStream)

list.parallelStream()
    .map(...)
    .forEach(...);
 
  • 내부적으로 Fork/Join 프레임워크 사용
  • 대용량 데이터 처리 시 유용하지만, 무조건 빠르지는 않음 → 병렬 처리 오버헤드 고려해야 함

🧠 5. Stream 사용 시 주의할 점

항목 주의사항
스트림 재사용 한 번 소비되면 재사용 불가 (새로 얻어야 함)
상태 있는 연산 peek() 같은 연산은 디버깅 외에는 주의
성능 소규모 데이터엔 오히려 for문보다 느릴 수 있음
Null 처리 NPE 방지를 위해 Optional과 함께 쓰면 좋음

 

📦 6. 실전 예제: 객체 리스트 필터링 및 정렬

class User {
    String name;
    int age;

    // 생성자, getter 생략
}

List<User> users = List.of(
    new User("Alice", 25),
    new User("Bob", 18),
    new User("Charlie", 30)
);

List<String> adultNames = users.stream()
    .filter(user -> user.age >= 20)
    .sorted(Comparator.comparingInt(user -> user.age))
    .map(user -> user.name)
    .collect(Collectors.toList());

결과: ["Alice", "Charlie"]


🧩 7. Stream vs 기존 반복문

기준 for-loop Stream
코드 길이 길어짐 간결
가독성 낮음 높음
병렬 처리 복잡 쉬움 (parallelStream)
디버깅 쉬움 어렵기도 함 (중간 연산 추적 어려움)

 

✍️ 마무리

Java Stream은 선언형 프로그래밍의 핵심 기능으로, 많은 반복문 로직을 간결하게 바꿀 수 있는 도구입니다.
하지만 모든 경우에 Stream이 정답은 아니며, 적절한 상황에서 for문과 병행해서 쓰는 것이 좋습니다.

“Stream을 이해하면 코드가 간결해지고, 오히려 더 깊은 객체지향적 사고로 나아갈 수 있다.”

LIST