[JAVA]자바 Stream 사용방법 2편(중간연산)
스트림 관련글
JAVA[자바] Stream 사용방법 1편(생성하기)
JAVA[자바] Stream 사용방법 3편(최종연산)
스트림 중간연산
스트림 중간연산은 생성한 스트림을 어떻게 가공할지 정하는 과정이다. 참고로 중간연산은 여러번 사용할 수 있다!
1
List<String> names = Arrays.asList("Eric", "Elena", "Java");
위의 List 를 기준으로 stream 중간연산 함수에 대해 설명하겠다.
Filtering
1
Stream<T> filter(Predicate<? super T> predicate);
Predicate
는 boolean
형을 리턴해주는 함수형 인터페이스이다. 간단히 말해서 필터링을 위해 어떤함수를 넣을때, 그 함수는 boolean
값을 리턴해줘야 한다.
1
2
3
String
Stream<String> stream = names.stream()
.filter(name -> name.contains("a")) // Elena, Java;
이름에 “a” 가 포함되는 모든 문자열을 필터링한다. 만약 더 복잡한 조건을 원한다면 직접 함수를 커스텀해서 넣어줄 수 있다.
1
2
3
4
5
6
7
Stream<String> stream = names.stream()
.filter(name -> {
if (name.contains("E") && !name.contains("r")) {
return true;
}
return false;
}); // Elena
이름에 “E” 를 포함하고, “r” 를 포함하지 않는 이름으로 필터링 했다. 해당 부분을 외부함수로 빼내면 좀 더 객체지향적으로 만들 수 있다.
Mapping
1
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Function<? super T, ? extends R>
는 매개변수를 받아서 값을 리턴해주는 함수를 뜻한다. 즉 원하는 데이터형식으로 바꿔주는 함수를 넣어주면 해당 형식의 스트림으로 반환을 해준다.
1
2
3
4
Stream<String> stream =
names.stream()
.map(String::toUpperCase);
// [ERIC, ELENA, JAVA]
map
에 String::toUpperCase
함수를 넣어주어 각각의 이름을 대문자로 변환시켜서 반환해준다. 이렇게 다른 형태로 값을 변경할때 사용한다.
그리고 좀 더 복잡한 flatmap
도 있습니다.
1
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
mapper 에 값을 보시면 Function<? super T, ? extends Stream<? extends R>>
으로 기존 map
과 다르게 ? extends Stream<>
으로 한번 더 감싸주었다. 그런데 리턴값은 map
과 똑같이 Stream<>
이다. 결과적으로 중첩된 구조를 한층 벗겨주는 역할한다.
1
2
3
4
5
6
List<List<String>> list =
Arrays.asList(Arrays.asList("a", "1"), Arrays.asList("b", "2")); // [[a, 1], [b, 2]]
List<String> flatList = list.stream() // Stream<List<String>>
.flatMap(s -> s.stream()) // Stream<String>
.collect(Collectors.toList()); // [a, 1, b, 2]
예제를 보면 이중리스트를 flatMap 을 이용해 하나의 리스트로 바꿔준것을 볼 수 있다. 이중리스트 구조를 단일 리스트로 구조를 한층 벗겨냈다.
Sorting
1
2
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
기본 sorted
함수는 오름차순으로 정렬해준다. 이 외의 다른 방식의 정렬은 Comparator
를 이용해서 정렬할 수 있다. 정렬 기준에대해 자세히 알고싶다면 JAVA[자바] Comparable 과 Comparator 참고하자.
1
2
3
4
names.stream()
.sorted((s1, s2) -> s2.length() - s1.length())
.collect(Collectors.toList());
//Elena, Eric, Java
Comparator
를 이용할 경우 정렬 기준을 직접 작성할 수 있다. 물론 기본으로 Comparator
에서 기본으로 제공하는 함수도 있다. 예를 들어 Comparator.reverseOrder()
를 사용하면 내림차순으로 정렬이 가능하다.
Boxed
기본스트림을 다른 타입으로 변경해줍니다.
1
2
3
4
5
IntStream.of(14, 11, 20, 39, 23) // IntStream
.sorted() // IntStream
.boxed() // Stream<Integer>
.collect(Collectors.toList());
// [11, 14, 20, 23, 39]
IntStream
을 정렬을 한뒤 리스트 형태로 반환할려고 했다. ()collect(Collectors.toList())
최종연산으로 값을 리스트로 반환해준다)그런데 IntStream
에는 collect
함수를 지원하지 않기때문에 boxed
를 이용해 Stream<Integer>
로 바꿔준뒤 사용해야한다. 이런 IntSream, DoubleStream, LongStream
형식 으로 지원하지 않는 함수가 있으면 boxed
를 사용해 타입을 바꾸면 사용할 수 있다.
Peek
stream 의 각각의 연산을 대상으로 연산 한다. 결과값에 영향을 끼치지 않아 중간에 값을 확인할때 사용한다.
하지만 결과값에 영향을 끼치지 않을뿐, 해당 함수를 이용해 다른 리스트에 값을 추가하는 등 연산을 할 수 있다. 물론 권장 하는 방식은 아니다.
1
2
3
int sum = IntStream.of(1, 3, 5, 7, 9)
.peek(System.out::println)
.sum();
Distinct
요소내 중복값을 제거해준다. 참고로 객체간의 비교를 하면 equals()
로 비교를하기 때문에 원하는 결과를 얻을려면 해당 클래스에 equals()
오버라이딩이 필요하다.
1
2
3
4
5
List<String> stringNumbers = Arrays.asList("1", "2", "3", "1");
List<String> distinctNumbers = stringNumbers.stream()
.distinct()
.collect(Collectors.toList()); // 1, 2, 3
Limit
결과값의 개수를 제한한다.
1
2
Stream<String> generatedStream =
Stream.generate(() -> "g").limit(3); // [g, g, g]
Skip
첫번째 요소부터 설정값까지 건너뛴다.
1
2
Stream<String> stream = names.stream()
.skip(1); // "Elena", "Java"