우와한 개발자

[Java] 스트림(Stream)이란? 개념과 주요 메서드, 직렬/병렬 스트림

by 우와한개발자

[ 스트림 Stream ]

1. 스트림 Stream 이란?

  • 스트림(Stream)은 요소들의 연속적인 집합을 나타냄.
  • 자바의 스트림(Stream) API는 컬렉션과 배열과 같은 데이터를 처리하는 데 사용
  • 주로 데이터 변환, 필터링, 정렬, 수집(가공한 데이터), 통계 등에 사용
  • Java 8 부터 적용
  • 성능이 좋기 때문에 반복문보다는 stream 사용 권장

 

2. 스트림 Stream 의 특징

  • 선언적 프로그래밍
  • 지연 평가(Lazy Evaluation) : 필요할 때만 연산 수행. 즉, 최종연산이 호출될 때까지 중간연산 수행하지 않아 불필요한 계산 줄이고 성능 향상
  • 함수형 스타일 : 함수형 인터페이스 사용해 람다 표현식과 메서드 참조를 통해 가독성 향상
  • 재사용 안됨 : 다시 사용하려면 새로운 스트림 생성 해야함.
Stream<String>	stream	=	list.stream();
stream.forEach(System.out::println);
//	stream.forEach(System.out::println);	//	Error

 

3. 스트림 Stream 연산

1) 스트림 생성

생성 방법 코드 예시 설명
컬렉션 기반 list.stream() List, Set 등 컬렉션에서 스트림 생성
배열 기반 Arrays.stream(arr) 배열을 스트림으로 변환 (int[], String[] 등)
Builder 사용 Stream<String>builder()
.add("a")
.add("b")
.build()
점진적으로 값 추가 후 스트림 생성
Stream.of() Stream.of(1, 2, 3) 직접 요소를 지정하여 생성
병렬 스트림 list.parallelStream() 병렬 처리용 스트림 생성
무한 스트림 (iterate) Stream.iterate(0, n -> n + 1) 초기값과 람다식을 이용해 연속된 값 생성
무한 스트림 (generate) Stream.generate(Math::random) 공급자 함수로 계속 값을 생성
파일 기반 Files.lines(Path) 텍스트 파일의 각 줄을 스트림으로 읽기 (IOException 처리 필요)

 

2) 중간 연산

  • 자주 사용되는 중간연산 메서드
메서드 설명 예시
filter(Predicate) 조건에 맞는 요소만 포함하는 스트림 반환 .filter(s -> s.startsWith("A"))
map(Function) 각 요소에 특정 함수를 적용해 변형한 스트림 반환 .map(s -> s.toUpperCase())
sorted() 정렬 .sorted() 또는
.sorted(Comparator.reverseOrder())
distinct() 중복 제거(hascode와 equals로 비교) .distinct()
limit(n) 앞에서 n개만 가져옴 .limit(3)
skip(n) 앞에서 n개 건너뜀 .skip(2)
flatMap(Function) 각 요소를 스트림으로 펼친 뒤 하나로 합침(중첩 제거) .flatMap(
   s -> Arrays.stream(s.split(" ")))
peek(Consumer) 스트림의 각 요소를 소비하면서 원본 스트림을 반환
(중간 결과값 찍어볼려고 디버깅용으로만 거의 씀.)
.peek(x -> System.out.println(x))

 

3) 최종 연산

  • 자주사용되는 최종연산 메서드
메서드 설명 예시
forEachOrdered(Consumer) 각 요소에 대해 순서대로 동작 수행(순서 보장)  .forEachOrdered(System.out::println)
collect(Collector) 스트림 요소를 컬렉션/문자열 등으로 수집 .collect(Collectors.toList())
toArray() 요소를 Object[]로 반환 .toArray()
toArray(IntFunction<A[]>) 원하는 타입 배열로 반환 .toArray(String[]::new)
reduce(BinaryOperator) 요소를 하나로 결합(집계)하여 반환 .reduce(Integer::sum)
reduce(identity, BinaryOperator) 초기값 포함해 집계 .reduce(0, Integer::sum)
anyMatch(Predicate) 하나라도 조건 만족하면 true .anyMatch(n -> n > 5)
allMatch(Predicate) 모두 조건 만족하면 true .allMatch(n -> n > 5)
noneMatch(Predicate) 모두 조건 불만족이면 true .noneMatch(n -> n < 0)
findFirst() 첫 번째 요소 반환 .findFirst().orElse("없음")
findAny() 임의의 요소 하나 반환 .findAny().orElse("없음")
count() 요소 개수 반환 .count()
max(Comparator) 최대 요소 반환 .max(Integer::compareTo)
min(Comparator) 최소 요소 반환 .min(Integer::compareTo)
forEach(Consumer) 각 요소에 대해 주어진 동작 수행 .forEach(System.out::println)

 

4. 스트림에서 Collectors 클래스

메서드 설명 예시
toList() 스트림 요소를 List로 수집 stream.collect(Collectors.toList())
toSet() 스트림 요소를 Set으로 수집 stream.collect(Collectors.toSet())
toMap(keyMapper, valueMapper) 키/값 매핑하여 Map 생성 stream.collect(Collectors.toMap(String::length, s -> s))
joining() 문자열 연결 stream.collect(Collectors.joining())
joining(delimiter) 구분자 포함 문자열 연결 Collectors.joining(",")
joining(delimiter, prefix, suffix) 접두사/접미사 포함 연결 Collectors.joining(",", "[", "]")
counting() 요소 개수 반환 stream.collect(Collectors.counting())
summarizingInt(mapper) Int 통계 정보 반환 Collectors.summarizingInt(String::length)
summarizingLong(mapper) Long 통계 정보 반환 Collectors.summarizingLong(User::getId)
summarizingDouble(mapper) Double 통계 정보 반환 Collectors.summarizingDouble(Product::getPrice)
averagingInt(mapper) 평균(double) 반환 Collectors.averagingInt(String::length)
averagingLong(mapper) 평균(double) 반환 Collectors.averagingLong(User::getId)
averagingDouble(mapper) 평균(double) 반환 Collectors.averagingDouble(Product::getPrice)
groupingBy(classifier) 기준값으로 그룹화(Map<K, List>) Collectors.groupingBy(String::length)
groupingBy(classifier, downstream) 그룹화 후 추가 집계 Collectors.groupingBy(String::length, Collectors.counting())
partitioningBy(predicate) true/false 기준 분할 Collectors.partitioningBy(s -> s.length() > 3)
partitioningBy(predicate, downstream) 분할 후 추가 집계 Collectors.partitioningBy(s -> s.length()>3, Collectors.counting())
reducing(BinaryOperator) 요소를 하나로 결합(Optional 반환) Collectors.reducing((a,b) -> a+b)
reducing(identity, BinaryOperator) 초기값 포함 결합 Collectors.reducing(0, Integer::sum)
reducing(identity, mapper, op) 매핑 후 결합 Collectors.reducing(0, String::length, Integer::sum)

 

5. 직렬 스트림과 병렬 스트림

1) 직렬 스트림(Sequential Stream)

  • 기본적으로 스트림은 직렬로 처리.
  • 스트림의 각 요소가 순서대로 처리.
 List<String>	list	=	Arrays.asList("a",	"b",	"c");
list.stream().forEach(System.out::println);

 

2) 병렬 스트림(Parallel Stream)

  • 병렬 스트림은 여러 스레드에서 스트림의 요소를 동시에 처리.
  • 병렬 스트림은 큰 데이터셋을 처리할 때 유용하지만, 스레드간의 동기화 문제 발생 가능
 List<String>	list	=	Arrays.asList("a",	"b",	"c");
list.parallelStream().forEach(System.out::println);

 

구분 일반 스트림 (stream()) 병렬 스트림 (parallelStream())
실행 방식 단일 쓰레드 멀티 쓰레드 (Fork/Join Pool)
처리 순서 유지됨 보장되지 않음 (순서가 중요하면 위험)
속도 느릴 수 있음 빠를 수 있음 (CPU 코어 많을수록)
병렬 처리 ❌ 안됨 ✅ 자동 병렬 처리
사용 예 간단한 순차 처리 CPU 코어 활용이 중요한 작업, 대용량 처리

 

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기