지난 시간에는 스트림을 생성하는 방법까지 알아보았다.
오늘은 스트림의 중간연산 중 스트림 자르기와 걸러내기에 대해 기록하려고 한다.
이전 포스팅에서도 얘기한 바 있지만 중간연산은 말 그대로 중간에서 연산만 해주는 역할이다. 스트림의 최종연산이 호출되기 전까지는 중간연산은 수행되지 않는다. 최종연산을 수행할 때 중간 중간에 이런 연산을 하겠다 라는 선언만 해주는 것이다.
스트림의 중간연산에는 어떤 것이 있는 지 알아보자.
스트림을 잘라서 연산할 수 있다. skip()과 limit()이 있는데 둘의 사용법은 매우 간단하다.
skip()은 스트림의 첫 요소부터 지정한만큼 생략(skip)하고 연산을 시작할 수 있다.
Stream<Integer> intStream = Arrays.stream(new Integer[]{1,2,3,4,5});
intStream.skip(2).forEach(System.out::print); // 첫 2개를 생략!
결과 : 345
limit()은 다음 스트림으로 넘겨주는 요소의 개수를 지정한 만큼 제한한다.
IntStream randomStream = new Random().ints(1,11); // 1부터 11까지 랜덤으로 출력해주는 Random 클래스의 스트림
randomStream.limit(5).forEach(System.out::println); // 5개로 제한
결과 : 2 9 10 1 5
이전 장에서 난수 스트림을 만드는 방법에 대해 알아보았다. 난수를 만들 때 limit()
중간 연산을 이미 사용한 적이 있다. 난수 스트림은 무한 스트림이기때문에 개수 제한을 두지 않으면 골치아프다.
혹시 기억이 안난다거나 보지 않았다면 스트림 난수 만들기를 참조하길 바란다.
중간 요소의 이름들이 매우 직관적이다.
filter()
는 말 그대로 필터링하는 것이다. 어떤 조건(predicate 람다식)에 해당하는 요소만 남기고 나머지는 걸러낸다.
Stream<T> filter(Predicate<? super T> predicate)
Predicate 람다식도 이전 포스팅에서 얘기했으므로 자세한 설명은 생략하겠다. 지금은 조건식을 통해 boolean을 반환하는 람다식이라고만 알면 된다.
Stream<Integer> intStream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5});
intStream.filter(i->i>3).forEach(System.out::println);
결과: 4 5
filter()
를 자세히 보면 매개변수로 각 요소(i)를 가져와서 i>3
인 경우만 남기고 나머지는 걸러낸다(필터링한다).
필요하다면 filter()
를 여러 번 사용해도 상관없다.
IntStream intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i->i % 2==0).filter(i->i > 5).forEach(System.out::println);
결과: 6 8 10
IntStream.rangeClosed()
는 특정범위의 정수를 순차적으로 스트림 요소에 저장하여 스트림을 생성하는 것을 말한다.(참조)
rangeClosed(1,10)
은 1부터 10까지이다. range()
라면 1부터 9까지를 의미한다.
어쨌든 중요한 것은 filter가 두 개 들어갔다는 것이다. 첫번째 filter는 연속된 정수 1~10 중에 짝수인 것만 남긴다. 그럼 홀수는 모두 걸러지고 스트림 요소에 (2, 4, 6, 8, 10)이 남아있을 것이다. 그 다음 필터에서는 5보다 큰 수만 남긴다. 그럼 스트림 요소에는 (6, 8, 10)만 남는다. 때문에 출력해보면 6 8 10이 나온다.
distinct()
는 중복제거이다.
Stream<T> distinct()
Stream<Integer> intStream = Arrays.stream(new Integer[]{1, 1, 2, 2, 2, 3});
intStream.distinct().forEach(System.out::println);
결과 : 1 2 3
스트림이 distinct()
연산을 만나기 전까진 {1, 1, 2, 2, 2, 3}의 요소를 가지고 있다. distinct()
를 통해 중복된 요소들을 걸러내면 결과는 1, 2, 3이 남는다.
스트림의 갈 길은 아직 멀고도 험하다. 특히 sorted()
중간연산이나 collect()
최종연산을 이해하기 위해서는 Comparator에 대한 이해가 필수적으로 필요하다. 다음 포스팅에는 Stream과 직접적인 관계는 없지만 자주 등장하는 Comparator에 대해 알아보고자 한다.
참조 : 자바의 정석 3rd Edition