Stream API에서 생성된 초기 스트림은 중개 연산을 통해서 새로운 스트림으로 변환된다.
이러한 중개 연산은 스트림을 전달받아서 스트림을 반환하므로, 중개 연산은 연속으로 사용할 수 있다.
또한 중개 연산은 filter-map 기반의 API를 사용함으로 지연(lazy) 연산을 통해 성능을 최적화 할 수 있다.
아래는 대표적인 중개 연산에 대한 설명이다.
filter 메서드는 해당 스트림에서 조건에 맞는 요소만으로 구성된 새로운 스트림을 반환한다.
IntStream.of(1, 2, 3, 4)
.filter(n -> n % 2 != 0)
.forEach(System.out::println);
// result
1
3
distinct 메서드는 해당 스트림에서 중복된 요소가 제거된 새로운 스트림을 반환한다.
IntStream.of(1, 2, 1, 3, 4, 1, 5)
.distinct()
.forEach(System.out::println);
// result
1
2
3
4
5
map 메서드는 해당 스트림의 요소들을 주어진 함수에 인수로 전달해서 그 반환 값들로 이루어진 새로운 스트림을 반환한다.
Stream.of("hi", "ho", "hu")
.map(s -> s.length(System.out::println);
// result
2
2
2
스트림의 요소가 배열일 경우, flatMap 메서드를 사용해서 각 배열의 반환값을 하나의 스트림으로 합쳐서 리턴할 수 있다.
우선 map 을 사용하면 어떻게 나오는지 알아보자.
String [] animal = ["hi", "ho"]
Arrays.stream(animal)
.map(arr -> arr.split("")) // Stream<String[]>
.collect(Collectors.toList()) // List<String[]>
.forEach(arr -> System.out.println(Arrays.toString(arr)));
// result
[h, i]
[h, o]
배열이 따로따로 생성되어서 split 된 결과가 나오는 것을 알 수 있다.
그럼 flatMap 을 사용해보자.
String [] animal = {"hi","ho"};
Arrays.stream(animal)
.map(arr -> arr.split("")) // Stream<String[]>
.flatMap(Arrays::stream) // Stream<String>
.collect(Collectors.toList()) // List<String>
.forEach(System.out::println);
// result
h
i
h
o
스트림을 각각 IntStream, LongStream, DoubleStream으로 변환해주는 메서드이다.
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
// list to arr(Integer)
int[] arr = intList.stream() // Stream<Integer>
.mapToInt(Integer::intValue) // IntStream
.toArray();
위에서 mapToInt를 통해서 Integer -> int로 바꿔주는 중개 연산을 수행하는 것을 확인할 수 있다.
위와 반대로 기본형 특화 스트림을 일반 스트림으로 변환해주는 메서드이다.
IntStream.of(1, 2, 3, 5) // IntStream
.mapToObj(i -> i + "번") // Stream<String>
.forEach(System.out::println);
// result
1번
2번
3번
5번
IntStream, LongStream, DoubleStream과 같은 기본 타입에 특화된 스트림을 일반 스트림으로 변환할 수 있다.
// Int 타입의 스트림을 일반 스트림으로 변환
Stream<Integer> boxedStream = IntStream.range(0, 3).boxed();
limit 메서드는 해당 스트림의 첫 번째 요소부터 전달되는 개수만큼의 요소만으로 이루어진 새로운 스트림을 리턴한다.
IntStream.range(0, 10)
.limit(5)
.forEach(System.out::println);
// result
0
1
2
3
4
해당 스트림의 첫 번째 요소부터 전달된 개수 만큼의 요소를 제외한 스트림을 리턴한다.
IntStream.range(0, 10)
.skip(3)
.forEach(System.out::println);
// result
3
4
5
6
7
8
9
sorted는 스트림을 주어진 비교자(Comparator)를 통해서 정렬한다.
비교자가 없다면 기본적으로 사전순으로 정렬된다.
Stream.of("a", "cd", "ezxc", "der")
.sorted()
.forEach(System.out::println);
System.out.println();
Stream.of("a", "cd", "ezxc", "der")
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
// result
a
cd
der
ezxc
ezxc
der
cd
a
peek은 연산과 연산 사이에 정상적으로 연산이 되었는지 확인할 때 사용한다.
forEach()와 달리 스트림의 요소를 소모하지 않으므로 연산 사이에 여러 번 끼워넣어도 문제가 되지 않는다.
고로 디버깅할 때 자주 사용된다.
IntStream.of(1,2,3,4,5)
.filter(a -> a%2 == 0)
.peek(System.out::println)
.forEach(s -> System.out.println("peek test!"));
// result
2
peek test!
4
peek test!
기억해야할 점은, 중개 연산 메소드는 최종 연산 메소드가 실행되지 않으면 동작하지 않는다는 점이다.
최종 연산 메서드가 없으면 최종 연산 메서드가 실행되기 전까지 지연(lazy) 된다.