3. 스트림의 중간연산
① 스트림 자르기 - skip(), limit()
Stream<T> skip(long n)
Stream<T> limit(long maxSize)
// 기본형 스트림에도 skip()과 limit()이 정의되어 있으며, 반환 타입이 기본형 스트림이다.
IntStream skip(long n)
IntStream limit(long maxSize)
▶ skip()과 limit()은 스트림의 일부를 잘라낼 때 사용한다.
ex) skip(3)은 처음 3개의 요소를 건너뛴다.
limit(5)는 스트림의 요소를 5개로 제한한다.
// 10개의 요소를 가진 스트림에 skip(3)과 limit(5)를 순서대로 적용하면
// 4번째 요소부터 5개의 요소를 가진 스트림이 반환된다.
IntStream intStream = IntStream.rangeClosed(1, 10); // 1~10의 요소를 가진 스트림
intStream.skip(3).limit(5).forEach(System.out::print); // 45678
② 스트림의 요소 걸러내기 - filter(), distinct()
Stream<T> filter(Predicate<? super T> predicate)
Stream<T> distinct()
▶ filter() : 주어진 조건(Predicate)에 맞지 않는 요소를 걸러낸다.
▶ distinct() : 스트림에서 중복된 요소를 제거한다.
// filter()는 매개변수로 Predicate를 필요로 하는데, 연산결과가 boolean인 람다식을 사용해도 된다.
IntStream intStream = IntStream.rangeClosed(1, 10); // 1~10
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
// filter()를 다른 조건으로 여러 번 사용할 수도 있다.
intStream.filter(i->i%2!=0 && i%3!=0).forEach(System.out::print); // 157
intStream.filter(i->i%2!=0).filter(i->i%3!=0).forEach(System.out::print); // 157
③ 정렬 -sorted()
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator>
// ex) 문자열 스트림을 String에 정의된 기본 정렬(사전순 정렬)로 정렬해서 출력한다.
Stream<String> strStream = Stream.of("dd","aaa","CC","cc","b");
streStream.sorted().forEach(System.out::print); // CCaaabccdd
▶ sorted() : 스트림을 정렬한다.
▶ sorted()는 지정된 Comparator로 스트림을 정렬하는데, Comparator 대신 int값을 반환하는 람다식을 사용하는 것도 가능하다.
▶ Comparator를 지정하지 않으면 스트림 요소의 기본 정렬 기준(Comparable)으로 정렬한다. 단, 스트림의 요소가 Comparable을 구현한 클래스가 아니면 예외가 발생한다.
< 문자열 스트림을 정렬하는 다양한 방법 >
문자열 스트림 정렬 방법 | 출력결과 |
strStream.sorted() // 기본 정렬 strStream.sorted(Comparator.naturalOrder()) // 기본 정렬 strStream.sorted((s1,s2) -> s1.compareTo(s2)) // 람다식도 가능 strStream.sorted(String::compareTo) // 위의 문장과 동일 |
CCaabccdd |
strStream.sorted(Comparator.reverseOrder()) // 기본 정렬의 역순 strStream.sorted(Comparator.<String>naturalOrder().reversed()) |
ddccbaaaCC |
strStream.sorted(String.CASE_INSENSITIVE_ORDER) // 대소문자 구분안함 참고) String.CASE_INSENSITIVE_ORDER은 String클래스에 정의된 Comparator이다. |
aaabCCccdd |
strStream.sorted(String.CASE_INSENSITIVE_ORDER.reversed()) | ddCCccbaaa |
strStream.sorted(Comparator.comparing(String::length)). // 길이 순 정렬 strStream.sorted(Comparator.comparingInt(String::length)) // no오토박싱 |
bddCCccaaa |
strStream.sorted(Comparator.Comparing(String::length).reversed()) | aaaddCCccb |
※ JDK1.8부터 Comparator 인터페이스에 static메서드와 디폴트 메서드가 많이 추가되었는데, 이 메서드들을 이용하면 정렬이 쉬워진다.
<Comparator의 default 메서드>
- reversed()
- thenComparing(Comparator<T> other)
- thenComparing(Function<T,U> keyExtractor)
-thenComparing(Function<T,U> keyExtractor, Comparator<T> keyComp)
-thenComparingInt(ToIntFunction<T> keyExtractor)
-thenComparingLong(ToLongFunction<T> keyExtractor)
-thenComparingDouble(ToDoubleFunction<T> keyExtractor)
<Comparator의 static 메서드>
- naturalOrder()
- reverseOrder()
- comparing(Function<T,U> keyExtractor)
- comparing(Function<T,U> keyExtractor, Comparator<U> keyComparator)
- comparingInt(ToIntFunction<T> keyExtractor)
- comparingLong(ToLongFunction<T> keyExtractor)
- comparingDouble(ToDoubleFunction<T> keyExtractor)
- nullsFirst(Comparator<T> comparator)
- nullsLast(Comparator comparator)
// 가장 기본적인 메서드는 comparing()이다.
// 스트림의 요소가 Comparable을 구현한 경우, 매개변수 하나짜리를 사용
comparing(Function<T,U> keyExtractor)
// 스트림의 요소가 Comparable을 구현하지 않은 경우, 추가적인 매개변수로 정렬기준(Comparator)을 따로 지정
comparing(Function<T,U> keyExtractor, Comparator<U> keyComparator)
// 비교대상이 기본형인 경우, comparing()대신 아래의 메서드를 사용하면 오토박싱과 언박싱과정이 없어서 효율적이다.
comparingInt(ToIntFunction<T> keyExtractor)
comparingLong(ToLongFunction<T> keyExtractor)
comparingDouble(ToDoubleFunction<T> keyExtractor)
// 정렬 조건을 추가할 때는 thenComparing()을 사용한다.
thenComparing(Comparator<T> other)
thenComparing(Function<T,U> keyExtractor)
thenComparing(Function<T,U> keyExtractor, Comparator<T> keyComp)
// ex) 학생 스트림(studentStream)을 반(ban)별, 성적(totalScore)순, 이름(name)순으로 정렬하여 출력
studentStream.sorted(Comparator.comparing(Student::getBan)
.thenComparing(Student::getTotalScore)
.thenComparing(Student::getName))
.forEach(System.out::println);
④ 변환 - map()
Stream<R> map(Function<? super T, ? extends R> mapper)
▶ map() : 스트림의 요소에 저장된 값 중에서 원하는 필드만 뽑아내거나 특정 형태로 변환한다. 매개변수로 T타입을 R타입으로 변환해서 반환하는 함수를 지정해야한다.
// ex) File의 스트림에서 파일의 이름만 뽑아서 출력
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
// map()으로 Stream<File>을 Stream<String>으로 변환
Stream<String> filenameStream = fileStream.map(File::getName);
// 스트림의 모든 파일이름을 출력
filenameStream.forEach(System.out::println);
// ex) File의 스트림에서 파일의 확장자만을 뽑은 다음 중복을 제거해서 출력한다.
// map()도 filter()처럼 하나의 스트림에 여러 번 적용할 수 있다.
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.map(s->s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.map(String::toUpperCase). // 모두 대문자로 변환
.distinct() // 중복 제거
.forEach(System.out::print); // JAVABAKTXT
⑤ 조회 - peek()
▶ peek() : 연산과 연산 사이에 올바르게 처리되었는지 확인할 때 사용한다.
▶ filter()나 map()의 결과를 확인할 때 유용하게 사용될 수 있다.
▶ forEach()와 달리 스트림의 요소를 소모하지 않으므로 연산 사이에 여러 번 끼워 넣을 수 있다.
// ex) File의 스트림에서 확장자만 뽑아서 출력
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.peek(s->System.out.printf("filename = %s%n", s)) // 파일명을 출력한다.
.map(s->s.substring(s.indexOf('.')+1)) // 확장자만 추출
.peek(s->System.out.printf("extension = %s%n", s)) // 확장자를 출력한다.
.forEach(System.out::println);
⑥ mapToInt(), mapToLong(), mapToDouble()
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)
▶ Stream<T>타입의 스트림을 기본형 스트림으로 변환할 때 사용한다.
< 기본형 스트림이 제공하는 메서드 >
메서드 | 설명 |
int sum() | 스트림의 모든 요소의 총합 |
OptionalDouble average() | sum() / (double)count() |
OptionalInt max() | 스트림의 요소 중 제일 큰 값 |
OptionalInt min() | 스트림의 요소 중 제일 작은 값 |
주의) 이 메서드들은 최종연산이기 때문에 호출 후에 스트림이 닫힌다. 즉, 하나의 스트림에 sum()과 average()를 연속해서 호출할 수 없다. |
// sum()과 average()를 모두 호출해야할 때, summaryStatistics()라는 메서드를 사용하면 편리하다.
// IntSummaryStatistics는 숫자를 다루는 여러 메서드를 제공한다.
IntSummaryStatistics stat = scoreStream.summaryStatistics();
long totalCount = stat.getCount();
long totalScore = stat.getSum();
double avgScore = stat.getAverage();
int minScore = stat.getMin();
int maxScore = stat.getMax();
// IntStream -> Stream<T>로 변환
Stream<U> mapToObj(IntFunction<? extends U> mapper)
// IntStream -> Stream<Integer>로 변환
Stream<Integer> boxed()
⑦ flatMap() - Stream<T[]>를 Stream<T>로 변환
// 요소가 문자열 배열인 스트림
Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc","def","ghi"},
new String[]{"ABC","GHI","JKLMN"}
);
// 스트림의 요소를 변환해주는 map()과 배열을 스트림으로 만들어주는 Arrays.stream(T[])를 사용
// map()으로 변환한 결과 Stream<Stream<String>>이다. 즉, 스트림의 스트림이 된다.
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);
// flatMap()으로 변환한 결과 Stream<String>이다.
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);
// 요소의 타입이 Stream<String>인 스트림(Stream<Stream<String>>) -> Stream<String>으로 변환
// toArray()는 스트림을 배열로 변환해서 반환한다. 매개변수를 지정하지 않으면 Object[]을 반환함.
Stream<String> strStream = strStrStrm
.map(s -> s.toArray(String[]::new)) // Stream<Stream<String>> -> Stream<String[]>
.flatMap(Arrays::stream); // Stream<String[]> -> Stream<String>
'프로그래밍 > JAVA' 카테고리의 다른 글
[ JAVA ] 바이트기반의 보조스트림 (0) | 2023.04.10 |
---|---|
[ JAVA ] Optional<T>와 OptionalInt (0) | 2023.03.21 |
[ JAVA ] 스트림(stream) - ① (0) | 2023.03.19 |
[ JAVA ] 쓰레드의 실행제어 (0) | 2023.02.26 |
[ JAVA ] 지네릭스(Generics) - ③ (0) | 2023.02.07 |