10-7. Stream API

박은서's avatar
Feb 06, 2026
10-7. Stream API

1️⃣ Stream이란? (Java 기준)

1) 개념

컬렉션(또는 배열)의 데이터를 선언형으로 처리하기 위한 데이터 처리 파이프라인

2) 핵심 개념 요약

  • 데이터 저장 X → Stream은 데이터를 보관하지 않음
  • 함수형 처리filter, map 같은 함수 조합
  • 지연 연산(Lazy) → 최종 연산이 호출될 때까지 실행 안 됨
  • 1회용 → 한 번 사용하면 재사용 불가
  • 병렬 처리 가능parallelStream()
List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.stream() .filter(n -> n % 2 == 0) .map(n -> n * 10) .forEach(System.out::println);

2️⃣ Stream 처리 구조

데이터 소스 ↓ 중간 연산 (0개 이상) ↓ 최종 연산 (1개, 필수)
구분
특징
중간 연산
Stream 반환 (체이닝 가능)
최종 연산
결과 반환 또는 실행 후 종료

3️⃣ 자주 쓰이는 중간 연산 함수

1) filter(Predicate)

조건에 맞는 요소만 통과
stream.filter(x -> x > 10)
✔ Boolean 반환 함수 사용

2) map(Function)

요소를 다른 형태로 변환
stream.map(String::length)
1 → 1 변환

3) flatMap(Function)

중첩 구조를 평탄화
List<List<Integer>> list = List.of( List.of(1, 2), List.of(3, 4) ); list.stream() .flatMap(List::stream) .forEach(System.out::println);
List<List<T>> → Stream<T>

4) distinct()

중복 제거 (equals 기준)
stream.distinct()

5) sorted() / sorted(Comparator)

정렬
stream.sorted() stream.sorted(Comparator.reverseOrder())

6) limit(n) / skip(n)

개수 제한 / 건너뛰기
stream.limit(5) stream.skip(3)

4️⃣ 자주 쓰이는 최종 연산 함수

1) forEach(Consumer)

각 요소에 대해 동작 수행
stream.forEach(System.out::println);
⚠ 반환값 없음

2) collect(Collector)

Stream → 컬렉션 변환
List<String> result = stream.collect(Collectors.toList());
자주 쓰는 예:
Collectors.toSet() Collectors.toMap(k -> k.getId(), v -> v) Collectors.joining(", ")

3) reduce(BinaryOperator)

요소를 하나의 값으로 축소
int sum = stream.reduce(0, Integer::sum);

4) count()

요소 개수
long cnt = stream.count();

5) anyMatch / allMatch / noneMatch

조건 검사
stream.anyMatch(x -> x > 100); stream.allMatch(x -> x > 0); stream.noneMatch(x -> x < 0);

6) findFirst() / findAny()

요소 하나 찾기 (Optional 반환)
Optional<Integer> value = stream.findFirst();

5️⃣ Stream 사용 시 자주 하는 패턴

1) 컬렉션 필터링 + 변환

List<String> names = users.stream() .filter(u -> u.isActive()) .map(User::getName) .toList();

2) 합계 / 평균

int total = orders.stream() .mapToInt(Order::getPrice) .sum();

3) 그룹핑

Map<String, List<User>> byRole = users.stream() .collect(Collectors.groupingBy(User::getRole));

6️⃣ Stream 사용 시 주의점

1) ⚠️ 부작용(side effect) 최소화

// ❌ 지양 stream.forEach(x -> list.add(x));

2) ⚠️ Stream 재사용 불가

Stream<Integer> s = list.stream(); s.count(); s.forEach(...); // 예외 발생

3) ⚠️ 단순 반복은 for-loop이 더 명확한 경우도 있음

7️⃣ 실습

1) 가공

C:\workspace\spring_lab\skillapp\src\main\java\ex04\StreamEx01.java
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 1. 가공 List<Integer> newList = list.stream() // 개울에 던지기 .map(i -> i * 2) // 가공 or 연산 (N번) : 중간 연산 .toList(); // 수집 // for문으로 출력 newList.stream().forEach(i -> { System.out.println(i); }); } }
notion image
💡
Stream은 중간 연산을 여러 번 할 수 있다!

2) 필터

C:\workspace\spring_lab\skillapp\src\main\java\ex04\StreamEx01.java
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 2. filter List<Integer> newList2 = list.stream() .map(i -> i+1) .peek(i -> System.out.println(i)) // 한 줄만 적으면 중괄호도 생략 가능 .filter(i -> i < 3) .toList(); } }
notion image
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 2. filter List<Integer> newList2 = list.stream() .map(i -> i+1) .peek(i -> System.out.println(i)) // 한 줄만 적으면 중괄호도 생략 가능 .filter(i -> i < 3) .peek(i -> System.out.println(i)) // 두번째 peek만 작동 .toList(); } }
notion image
💡
peek 은 중간과정 확인용!!
확인했으면 주석처리
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 2. filter List<Integer> newList2 = list.stream() .map(i -> i+1) .peek(i -> System.out.print(i + " ")) // 한 줄만 적으면 중괄호도 생략 가능 .filter(i -> i < 3) .toList(); System.out.println(); for (Integer i : newList2) { System.out.print(i + " "); } } }
notion image

3) Max

C:\workspace\spring_lab\skillapp\src\main\java\ex04\StreamEx01.java
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 3. Max int maxNum = list.stream() .mapToInt(i -> i) .max() .getAsInt(); System.out.println(maxNum); } }
notion image

4) limit

C:\workspace\spring_lab\skillapp\src\main\java\ex04\StreamEx01.java
package ex04; import java.util.List; /** * Stream : 임시 개울 (임시 수도관) */ public class StreamEx01 { public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4); // 4. limit List<Integer> newList4 = list.stream() .limit(2) .peek(i -> System.out.print(i + " ")) .toList(); } }
notion image

5) flatMap

C:\workspace\spring_lab\skillapp\src\main\java\ex04\StreamEx02.java
package ex04; import java.util.List; public class StreamEx02 { public static void main(String[] args) { // [ [1, 2, 3] , [4, 5] , [6, 7, 8, 9] ] List<List<Integer>> matrix = List.of( List.of(1, 2, 3), List.of(4, 5), List.of(6, 7, 8, 9) ); // [ [1,2,3], [4,5], [6,7,8,9] ] // [1,2,3], [4,5], [6,7,8,9] // 1,2,3 4,5 6,7,8,9 // 1,2,3,4,5,6,7,8,9 // [1,2,3,4,5,6,7,8,9] List<Integer> newList = matrix.stream() .flatMap(list -> list.stream()) .toList(); for (Integer i : newList){ System.out.print(i + " "); } } }
notion image
package ex04; import java.util.List; public class StreamEx02 { public static void main(String[] args) { // [ [1, 2, 3] , [4, 5] , [6, 7, 8, 9] ] List<List<Integer>> matrix = List.of( List.of(1, 2, 3), List.of(4, 5), List.of(6, 7, 8, 9) ); // [ [1,2,3], [4,5], [6,7,8,9] ] // [1,2,3], [4,5], [6,7,8,9] // 1,2,3 4,5 6,7,8,9 // 1,2,3,4,5,6,7,8,9 // [1,2,3,4,5,6,7,8,9] List<Integer> newList = matrix.stream() .flatMap(list -> list.stream()) .filter(i -> i > 5) .toList(); for (Integer i : newList){ System.out.print(i + " "); } } }
notion image

6) 적용

C:\workspace\spring_lab\skillapp\src\main\java\ex01\CopyEx04.java
package ex01; import lombok.Data; import java.util.ArrayList; import java.util.List; @Data class Board { // 1:N의 관계에서 Board가 1 private int id; private String title; private String content; private List<Reply> replies = new ArrayList<>(); } @Data class Reply { // 1:N의 관계에서 Reply는 N private int id; private String comment; } @Data class DetailDTO { private int id; private String title; private String content; private List<String> comments = new ArrayList<>(); public DetailDTO(Board board) { this.id = board.getId(); this.title = board.getTitle(); this.content = board.getTitle(); this.comments = board.getReplies().stream() .map(i -> i.getComment()) .limit(2) .toList(); // for (int i = 0; i < board.getReplies().size(); i++) { // this.comments.add(board.getReplies().get(i).getComment()); // } // for문은 복잡! stream이 보기 편함! -> 그래서 stream 배울 것 // for (Reply reply : board.getReplies()) { // // if (id < 3) // if (reply.getId() < 3) { // this.comments.add(reply.getComment()); // } // // this.comments.add(reply.getComment()); // } } } public class CopyEx04 { public static void main(String[] args) { // 3. 클래스로 넘기기 Reply r1 = new Reply(); r1.setId(1); r1.setComment("댓글1"); Reply r2 = new Reply(); r2.setId(2); r2.setComment("댓글2"); Reply r3 = new Reply(); r3.setId(3); r3.setComment("댓글3"); Board board = new Board(); board.setId(1); board.setTitle("제목1"); board.setContent("내용1"); board.setReplies(List.of(r1, r2, r3)); DetailDTO detailDTO = new DetailDTO(board); System.out.println(detailDTO); } }
notion image
Share article