스트림(Stream)
스트림(Stream)은 Java 8에서 추가되었습니다.
개울, 시내, 줄기, 줄 등의 의미로 무엇인가가 줄줄이 이어져 있는 것을 의미합니다.
스트림(Stream)은 컬렉션과 같은 연속된 정보를 처리하는 데 사용합니다.
컬렉션에는 스트림을 사용할 수 있지만, 배열에는 스트림을 사용할 수 없습니다.
하지만, 배열을 컬렉션 List로 변환하는 방법이 존재합니다.
Integer[] values = { 1, 3, 5 };
List<Integer> list = new ArrayList<Integer>(Arrays.asList(values));
대표적으로 위와 같이 Arrays
클래스의 asList()
메서드로 변환이 가능합니다.
이 외에도 Stream
클래스를 이용하는 방법도 있습니다.
스트림의 구조
스트림은 다음과 같은 구조를 가집니다.
// ex
list.stream().filter(x-> x>10).count()
// 스트림 생성 : stream()
// 중개 연산 : filter()
// 종단 연산 : count()
- 스트림 생성 : 컬렉션의 목록을 스트림 객체로 변환합니다.
- 중개 연산 : 생성된 스트림 객체를 사용하여 중개 연산 부분에서 처리합니다. 이 부분에서는 아무런 결과를 리턴하지 못합니다.
- 종단 연산 : 중개연산에서 작업된 내용을 바탕으로 결과를 리턴 합니다.
주로 사용하는 연산자
연산자 | 설명 |
filter(pred) | 데이터를 조건으로 거를 때 사용 |
map(mapper) | 데이터를 특정 데이터로 변환 |
forEach(block) | for 루프를 수행하는 것처럼 각각의 항목을 꺼냄 |
flatMap(flat-mapper) | 스트림의 데이터를 잘게 쪼개서 새로운 스트림 제공 |
sorted(comparator) | 데이터 정렬 |
toArray(array-factory) | 배열로 변환 |
any / all / noneMatch(pred) | 일치하는 것을 찾음 |
findFirst / Any(pred) | 맨 처음이나 순서와 상관없는 것을 찾음 |
reduce(binop) / reduce(base, binop) | 결과를 취합 |
collect(collector) | 원하는 타입으로 데이터를 리턴 |
종단 연산(Terminal Operations)
forEach(Consumer)
forEach()
는 각 요소에 대해 지정된 동작을 파이프라인에서 처리하는 종단 연산입니다.
// 문자 배열을 Stream으로 변환
String[] colors = {"Red", "Green", "Blue", "Yellow"};
Stream<String> colorStream = Stream.of(colors);
// Stream의 각 요소에 대해 forEach연산 수행.
colorStream.forEach(System.out::println);
// 출력
// > Red
// > Green
// > Blue
// > Yellow
count()
Stream의
요소수를 long
타입으로 반환합니다.
// 문자 배열을 Stream으로 변환
String[] colors = {"Red", "Green", "Blue", "Yellow"};
Stream<String> colorStream = Stream.of(colors);
// Stream의 요소 수를 연산
long count = colorStream.count();
System.out.println("count = " + count); // "count = 4" 출력
collect(Collector)
Stream
의 요소를 수집합니다.
String[] colors = {"Red", "Green", "Blue", "Yellow", "Red", "Green"};
// toList() : 스트림 요소를 리스트로 수집
List<String> transList1 = Stream.of(colors).collect(Collectors.toList());
List<String> transList2 = Stream.of(colors).toList();
System.out.println("transList1 = " + transList1); // transList1 = [Red, Green, Blue, Yellow, Red, Green] 출력
System.out.println("transList2 = " + transList2); // transList2 = [Red, Green, Blue, Yellow, Red, Green] 출력
// toSet() : 스트림 요소를 셋으로 수집
Set<String> transSet = Stream.of(colors).collect(Collectors.toSet());
System.out.println("transSet = " + transSet); // transSet = [Red, Blue, Yellow, Green] 출력
// joining() : 문자열 스트림의 요소를 결합하여 하나의 문자열로 수집
String strJoin = Stream.of(colors).collect(Collectors.joining("_"));
System.out.println("strJoin = " + strJoin); // strJoin = Red_Green_Blue_Yellow_Red_Green 출력
// groupingBy() : 스트림 요소를 그룹화하여 맵으로 수집
Map<Integer, List<String>> lengthMap = Stream.of(colors).collect(Collectors.groupingBy(String::length));
System.out.println("lengthMap = " + lengthMap); // lengthMap = {3=[Red, Red], 4=[Blue], 5=[Green, Green], 6=[Yellow]} 출력
// partitioningBy() : 스트림 요소를 분하여 맵으로 수집
IntStream numbers = IntStream.rangeClosed(1, 10);
Map<Boolean, List<Integer>> evenOddMap = numbers.boxed().collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("Even numbers : " + evenOddMap.get(true)); // Even numbers : [2, 4, 6, 8, 10] 출력
System.out.println("Odd numbers : " + evenOddMap.get(false)); // Odd numbers : [1, 3, 5, 7, 9] 출력
Map<Boolean, List<String>> colorPartition = Stream.of(colors).collect(Collectors.partitioningBy(color -> color.length() > 3));
System.out.println("More than 3 characters : " + colorPartition.get(true)); // More than 3 characters : [Green, Blue, Yellow, Green] 출력
System.out.println("No more than 3 characters : " + colorPartition.get(false)); // No more than 3 characters : [Red, Red] 출력
// reducing() :
String chainColors = Stream.of(colors).collect(Collectors.reducing((s1, s2) -> s1 + s2)).orElse("");
System.out.println("chainColors : " + chainColors); // chainColors = RedGreenBlueYellowRedGreenv 출력
reduce(BinaryOperator)
Stream
의 요소를 차례대로 더해 나갑니다.
// 정수 배열을 Stream으로 변환
Integer[] numbers = {1, 2, 3, 4, 5};
Stream<Integer> numberStream = Stream.of(numbers);
Optional<Integer> sum = numberStream.reduce((a, b) -> {
int result = a + b;
System.out.println(a + " + " + b + " = " + result);
return result;
});
sum.ifPresent(result -> System.out.println("Sum of numbers: " + result));
// 출력
// > 1 + 2 = 3
// > 3 + 3 = 6
// > 6 + 4 = 10
// > 10 + 5 = 15
// > Sum of numbers: 15
min(Comparator), max(Comparator)
Stream
에서 최소값과 최대값을 찾습니다.
// 문자 배열을 Stream으로 변환
String[] colors = {"Red", "Green", "Blue", "Yellow"};
Stream<String> colorStream = Stream.of(colors);
Optional<String> minColor = colorStream.min(Comparator.naturalOrder());
minColor.ifPresent(result -> System.out.println("Min color (alphabetically): " + result));
// 닫힌 스트림은 재사용이 불가하므로 새로 할당
colorStream = Stream.of(colors);
Optional<String> maxColor = colorStream.max(Comparator.naturalOrder());
maxColor.ifPresent(result -> System.out.println("Max color (alphabetically): " + result));
// 출력
// > Min color (alphabetically): Blue
// > Max color (alphabetically): Yellow
findAny(), findFirst()
findFirst()
는 항상 Stream
의 첫 번째 요소를 반환하며, findAny()
는 임의의 요소를 반환합니다.
String[] colors = {"Red", "Green", "Blue", "Yellow"};
Optional<String> firstColor = Stream.of(colors).findFirst();
firstColor.ifPresent(color -> System.out.println("First color: " + color)); // "First color: Red" 출력
Optional<String> anyColor = Stream.of(colors).findAny();
anyColor.ifPresent(color -> System.out.println("Any color: " + color)); // 예: "Any color: Green" 출력
Optional<String> anyColorParallel = Stream.of(colors).parallel().findAny();
anyColorParallel.ifPresent(color -> System.out.println("Any color (parallel): " + color)); // 예: "Any color (parallel): Blue" 출력
allMatch(Predicate), anyMatch(Predicate), noneMatch(Predicate)
allMatch()
: 스트림의 모든 요소가 주어진 조건을 만족하는지 확인합니다.anyMatch()
: 스트림의 임의의 요소가 주어진 조건을 만족하는지 확인합니다.noneMatch()
: 스트림의 모든 요소가 주어진 조건을 만족하지 않는지 확인합니다.
Integer[] numbers = {2, 4, 6, 8, 10};
Stream<Integer> numberStream = Stream.of(numbers);
boolean allEven = numberStream.allMatch(n -> n % 2 == 0);
System.out.println("All numbers are even: " + allEven); // "All numbers are even: true" 출력
numberStream = Stream.of(numbers);
boolean anyGreaterThanFour = numberStream.anyMatch(n -> n > 4);
System.out.println("Any number greater than 4: " + anyGreaterThanFour); // "Any number greater than 4: true" 출력
numberStream = Stream.of(numbers);
boolean noneGreaterThanEleven = numberStream.noneMatch(n -> n > 11);
System.out.println("No number greater than 11: " + noneGreaterThanEleven); // "No number greater than 11: true" 출력
toArray()
특정 타입의 배열로 변환할 수 있습니다.
String[] colors = {"Red", "Green", "Blue", "Yellow"};
Stream<String> colorStream = Stream.of(colors);
Object[] colorArray = colorStream.toArray();
for (Object color : colorArray) {
System.out.println(color);
}
forEachOrdered(Consumer)
Stream
의 요소를 순서대로 처리합니다. 병렬 스트림에서도 순서가 유지됩니다.
Integer[] numbers = {1, 2, 3, 4, 5};
Stream<Integer> numberStream = Stream.of(numbers).parallel();
System.out.println("Parallel Stream with forEachOrdered:");
numberStream.forEachOrdered(System.out::println);
// 출력
// > Parallel Stream with forEachOrdered:
// > 1
// > 2
// > 3
// > 4
// > 5
iterator()
Stream
의 요소를 순차적으로 접근할 수 있는 Iterator
를 얻습니다.
Integer[] numbers = {1, 2, 3, 4, 5};
Stream<Integer> numberStream = Stream.of(numbers);
Iterator<Integer> iterator = numberStream.iterator();
System.out.println("Stream elements:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 출력
// > Stream elements:
// > 1
// > 2
// > 3
// > 4
// > 5
중개 연산(Intermediate Operations)
filter(Predicate)
주어진 조건에 맞는 요소들만을 포함하는 새로운 스트림을 생성합니다.
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.stream().filter(number -> number > 3).forEach(System.out::println);
// 출력
// > 4
// > 5
map(Function)
각 요소에 대해 주어진 함수 적용 결과를 포함하는 새로운 스트림을 생성합니다.
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.stream().map(number -> number * 2).forEach(System.out::println);
// 출력
// > 2
// > 4
// > 6
// > 8
// > 10
flatMap(Function)
각 요소에 대해 주어진 함수 적용 결과 스트림을 병합하여 새로운 스트림을 생성합니다.
List<String> sentences = Arrays.asList("Hello world", "Java is fun", "Stream API");
List<String> words = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.toList();
System.out.println("words = " + words); // words = [Hello, world, Java, is, fun, Stream, API] 출력
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("a", "b", "c"),
Arrays.asList("d", "e", "f"),
Arrays.asList("g", "h", "i")
);
List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList); // [a, b, c, d, e, f, g, h, i] 출력
distinct()
중복 요소를 제거한 새로운 스트림을 생성합니다.
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(3);
numbers.add(5);
numbers.stream().distinct().forEach(System.out::println);
// 출력
// > 1
// > 2
// > 3
// > 4
// > 5
sorted(), sorted(Comparator)
요소들을 정렬한 새로운 스트림을 생성합니다.
String[] words = {"banana", "apple", "grape", "orange"};
Stream<String> stream = Arrays.stream(words);
Stream<String> sortedStream = stream.sorted(Comparator.comparingInt(String::length).reversed());
sortedStream.forEach(System.out::println);
// 출력
// > banana
// > orange
// > apple
// > grape
peek(Consumer)
각 요소에 대해 주어진 동작을 수행하고, 동일한 요소를 포함하는 새로운 스트림을 생성합니다. 주로 디버깅 목적으로 사용됩니다.
int[] numbers = {1, 2, 3, 4, 5};
IntStream.of(numbers)
.peek(System.out::println)
.forEach(n -> {});
// 출력
// > 1
// > 2
// > 3
// > 4
// > 5
// 중개연산의 과정에서 요소가 확인 가능합니다.
limit(long maxSize)
주어진 최대 크기만큼의 요소를 포함하는 새로운 스트림을 생성합니다.
// 1부터 시작하는 무한 스트림 생성
Stream<Integer> infiniteStream = Stream.iterate(1, i -> i + 1).peek(infinite -> System.out.println("infinite : " + infinite));
Stream<Integer> limitedStream = infiniteStream.limit(5);
limitedStream.forEach(System.out::println);
// 출력
// > infinite : 1
// > 1
// > infinite : 2
// > 2
// > infinite : 3
// > 3
// > infinite : 4
// > 4
// > infinite : 5
// > 5
// 스트림에서는 계속 무한으로 스트림을 생성하다가 limit 연산으로 5까지 확인하고 stream을 종료하여 무한루프에 빠지지 않습니다.
skip(long )
처음 n개의 요소를 건너뛴 나머지 요소를 포함하는 새로운 스트림을 생성합니다.
Stream<Integer> numbersStream = Stream.iterate(1, i -> i + 1).limit(10);
numbersStream.skip(5)
.forEach(System.out::println);
// 출력
// > 6
// > 7
// > 8
// > 9
// > 10
이 외에도 더 많은 Stream API가 있습니다.
내용 참고 : 자바의 神 (로드북)
내용 참고 : 망나니 개발자(티스토리)
'자바 탐구' 카테고리의 다른 글
자바) Java 21의 주요 특징 (0) | 2024.07.28 |
---|---|
자바) Stream API - 2) 함수형 인터페이스(Functional Interface) (2) | 2024.06.09 |
자바) Stream API - 1) 람다 표현식(Lambda Expression) (0) | 2024.06.09 |
스프링) 스프링이란? (0) | 2024.05.14 |
JPA) N + 1 문제 (0) | 2023.08.01 |