스트림(Stream)은 병렬 또는 순차적으로 처리할 수 있는 요소 시퀀스입니다.
컬렉션, 배열, 파일, 난수 등 다양한 소스에서 생성할 수 있으며 map
, reduce
, filter
, sort
등의 기능적 스타일 작업을 지원합니다. 스트림은 지연(Lazy)되어 필요할 때만 요소를 계산합니다.
스트림의 map
과 컬렉션의 유사 메서드는 중요한 차이점이 있습니다.
replaceAll
은 컬렉션을 반환하고 즉시 수행됩니다.List<String> list = Arrays.asList("a", "b", "c");
// 스트림의 map
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
// 컬렉션의 replaceAll
list.replaceAll(String::toUpperCase);
System.out.println(list);
컬렉션에서 생성
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
배열에서 생성
int[] arr = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(arr);
intStream.forEach(System.out::println);
빌더 사용
Stream<String> builderStream = Stream.<String>builder()
.add("Apple")
.add("Banana")
.add("Melon")
.build();
builderStream.forEach(System.out::println);
generate() 메소드
Stream<String> generateStream = Stream.generate(() -> "Hello")
.limit(5);
generateStream.forEach(System.out::println);
iterate() 메소드
Stream<Integer> iterateStream = Stream.iterate(100, n -> n + 10)
.limit(5);
iterateStream.forEach(System.out::println);
empty() 메소드
Stream<String> emptyStream = Stream.empty();
System.out.println(emptyStream.count()); // 0
기본 타입 스트림
IntStream intStream = IntStream.range(1, 10);
intStream.forEach(System.out::println);
filter
List<String> names = Arrays.asList("Jun", "James", "Chris", "Uni");
names.stream()
.filter(name -> name.startsWith("J"))
.forEach(System.out::println); // Jun, James
map
List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
fruits.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // APPLE, BANANA, MELON, GRAPE
sorted
List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
numbers.stream()
.sorted()
.forEach(System.out::println); // 1, 2, 3, 4, 5
집계 연산 (count, sum, average, min, max)
int[] scores = {80, 90, 100, 70, 85};
IntSummaryStatistics stats = Arrays.stream(scores).summaryStatistics();
System.out.println("개수: " + stats.getCount());
System.out.println("합계: " + stats.getSum());
System.out.println("평균: " + stats.getAverage());
System.out.println("최대: " + stats.getMax());
System.out.println("최소: " + stats.getMin());
매칭 연산 (anyMatch, allMatch, noneMatch)
List<String> colors = Arrays.asList("red", "blue", "green", "yellow");
boolean result1 = colors.stream().anyMatch(color -> color.equals("red")); // true
boolean result2 = colors.stream().allMatch(color -> color.length() == 4); // false
boolean result3 = colors.stream().noneMatch(color -> color.startsWith("p")); // true
System.out.println(result1); // true
System.out.println(result2); // false
System.out.println(result3); // true
reduce
List<String> animals = Arrays.asList("dog", "cat", "bird", "fish");
String result4 = animals.stream()
.reduce("", (a, b) -> a + b);
System.out.println(result4); // dogcatbirdfish
collect
List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
List<String> result5 = fruits.stream()
.filter(fruit -> fruit.length() == 5)
.collect(Collectors.toList());
result5.forEach(System.out::println); // apple, grape
병렬 처리는 대용량 데이터 처리에 유용하며 스트림 API에서도 지원됩니다. parallel()
메소드를 호출하면 ForkJoin 프레임워크를 사용하여 여러 스레드가 작업을 수행합니다.
import java.util.Arrays;
public class ParallelStreamExample {
public static void main(String[] args) {
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
long start = System.currentTimeMillis();
long count = Arrays.stream(numbers)
.parallel()
.filter(n -> n % 2 == 0)
.count();
long end = System.currentTimeMillis();
System.out.println("짝수의 개수: " + count);
System.out.println("걸린 시간(ms): " + (end - start));
}
}
스트림은 데이터 처리의 효율성과 가독성을 높입니다. 병렬 처리 시 동기화 문제와 오버헤드를 고려해야 합니다. 중간 연산과 최종 연산의 차이를 이해하고 상황에 맞게 스트림을 활용하는 것이 중요합니다.