Java 스트림(Stream)은 Java 8에서 도입된 강력한 API로, 데이터를 선언적이고 간결하게 처리할 수 있도록 설계되었습니다. 스트림은 데이터 컬렉션(예: 리스트, 배열)을 처리하는 데이터 파이프라인으로 이해할 수 있습니다.
스트림을 사용하면 명령형 프로그래밍(루프나 조건문) 대신 선언적 스타일로 데이터를 처리할 수 있습니다.
스트림은 중간 연산(Intermediate Operation)과 최종 연산(Terminal Operation)으로 나뉩니다. 중간 연산은 스트림을 변환하거나 필터링하며, 최종 연산은 스트림을 닫고 결과를 반환합니다.
중간 연산은 필요할 때만 실행됩니다. 즉, 최종 연산이 호출될 때까지 실행되지 않습니다.
스트림 연산은 원본 데이터를 변경하지 않고 새로운 스트림을 생성합니다.
스트림은 한 번 사용되면 재사용할 수 없습니다.
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
// 리스트에서 생성
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> streamFromList = list.stream();
// 배열에서 생성
String[] array = {"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);
// 숫자 범위에서 생성
IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4
LongStream longStream = LongStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5
// 직접 생성
Stream<String> directStream = Stream.of("one", "two", "three");
// 빈 스트림 생성
Stream<Object> emptyStream = Stream.empty();
}
}
중간 연산은 새로운 스트림을 반환합니다. 대표적인 중간 연산은 다음과 같습니다:
List<String> names = Arrays.asList("John", "Jane", "Doe", "Jane");
List<String> result = names.stream()
.filter(name -> name.startsWith("J")) // J로 시작하는 이름 필터링
.map(String::toUpperCase) // 대문자로 변환
.distinct() // 중복 제거
.sorted() // 정렬
.collect(Collectors.toList()); // 리스트로 변환
System.out.println(result); // [JANE, JOHN]
최종 연산은 스트림을 닫고 결과를 반환합니다. 대표적인 최종 연산은 다음과 같습니다:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 짝수만 선택
.reduce(0, Integer::sum); // 합계 계산
System.out.println(sum); // 6 (2 + 4)
numbers.stream().forEach(System.out::println); // 요소 출력
스트림은 병렬 처리를 지원합니다. 병렬 스트림은 데이터를 분리하여 멀티스레드로 작업을 처리합니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream()
.map(n -> n * n)
.forEach(System.out::println); // 결과는 병렬 처리 순서로 출력