스트림의 연산(중간연산, 최종연산)
중간연산 | 최종연산 |
n번 | 1번 |
Stream을 반환 | 결과를 반환(int, Boolean ..) 스트림의 요소를 소모해서 최종연산 후에는 스트림이 닫히게 되고 더이상 사용 불가 |
최종 연산 | 설명 |
void forEach(Consumer<? super T> action) void forEachOrdered(Consumer<? super T> action) (순서유지, 병렬스트림) |
각 요소에 지정된 작업 수행 |
long count( ) | 스트림의 요소의 개수 반환 |
Optional<T> max(Comparator<? super T> comparator) Optional<T> min(Comparator<? super T> comparator) |
스트림의 최대값/최소값을 반환(정렬기준) |
Optional<T> findAny( ) // 아무거나 하나(병렬, filter와) Optional<T> findFirst( ) // 첫 번째 요소(직렬) |
스트림의 요소 하나를 반환 |
boolean allMatch(Predicate<T> p) // 모두 만족? boolean anyMatch(Predicate<T> p) // 하나라도 만족? boolean noneMatch(Predicate<T> p) // 모두 만족하지 않는지? |
주어진 조건을 모든 요소가 만족시키는지, 만족시키지 않는지 확인 |
Object[ ] toArray( ) A[ ] toArray(IntFUnction<A[ ]> generator) |
스트림의 모든 요소를 배열로 반환 |
Optional<T> reduce(BinaryOperator<T> accumulator) T reduce(T identity, BinaryOperator<T> accumulator) U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner) |
스트림의 요소를 하나씩 줄여가면서(리듀싱)계산한다. ex) sum, count |
R collect(Collector<T,A,R> collector) R collect(Supplier<R> supplier, BiCosumer<R, T> accumulator, BiConsumer<R,R> combiner) |
스트림의 요소를 수집한다. 주로 요소를 그룹화하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용된다. |
-> reduce와 collect가 최종 연산의 핵심!
1. forEach( )
- 스트림의 모든 요소에 지정된 작업을 수행 – forEach( ), forEachOrdered( )
- 반환 타입이 void이어서 스트림의 요소를 출력하는 용도로 많이 사용
- 중간연산 peek( )과 달리 스트림의 요소를 소모하는 최종연산
2. 조건검사
(1) 조건검사 - allMatch( ), anyMatch( ), noneMatch( )
(2) 조건에 일치하는 요소 찾기 – findFirst( ), findAny( )
- 결과가 null일 수 있기 때문에 Optional<T>를 사용. 주로 filter( )와 함께 사용한다.
- findFirst()는 조건에 일치하는 첫 번째 것을 반환한다.
- findFirst와 findAny의 차이는 병렬스트림의 사용 유무이다.
(3) 스트림의 요소를 하나씩 줄여가며 누적연산 수행, 최종결과 반환 – reduce( )
- 스트림의 요소를 줄여나가면서 연산을 수행하고 최종결과를 반환한다. 그래서 매개변수타입이 BinaryOperator<T>이다.
- 처음 두 요소를 가지고 연산한 결과를 가지고 그 다음 요소와 연산한다.
- 초기값(identity)와 어떤 연산(accumulator)으로 스트림의 요소를 줄여 나갈 것인지 정하면 됨
*reduce의 핵심*
- identity : 초기값
- accumulator : 이전 연산결과와 스트림의 요소에 수행할 연산
- combiner : 병렬 처리된 결과를 합치는데 사용할 연산(병렬 스트림)
-> 초기값이 있는 연산(T identity)과 없는 연산으로 나뉨
-> 초기값이 없을 땐 null이 있을 수도 있어서 Optional<T>를 반환
- max( )와 min( )의 경우, 초기값이 필요 없어서 Optional<T>를 반환하는 매개변수 하나짜리인 reduce( )를 사용하는 것이 낫다.
(단 intStream의 타입이 IntStream(기본형 스트림)일 경우OptionalInt)를 사용해야 한다. Stream<T>와 달리 IntStream(기본형)에 정의된 reduce( )의 반환타입이 OptionalInt이기 때문이다. 저장된 값을 꺼낼 때는 getAsInt()사용!!!
* reduce()가 동작하는 과정 *
int a = identity; // a는 누적결과를 저장할 변수, 초기값을 a에 저장한다.
for(int b : stream)
a = a + b; // 모든 요소의 값을 a에 누적한다, 만약 count이면 b가 1로 바뀌게 됨
3. collect()와 Collectors
- collect( )는 스트림의 요소를 수집하는 최종 연산으로 스트림의 요소를 수집하려면 어떻게 수집할 것인가에 대한 방법이 정의되어 있어야 한다. -> 이 방법을 정의한 것이 Collector 인터페이스
collect( ) | Collector를 매개변수로 하는 스트림의 최종연산 |
Collector | 인터페이스, 컬렉터는 이 인터페이스를 구현해야 한다. |
Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스이다. | |
Collectors | 클래스, static 메서드로 미리 작성된 컬렉터(Collector를 구현한 클래스)를 제공한다. |
- collect()의 매개변수 타입은 Collector인데, 매개변수가 Collector를 구현한 클래스의 객체이어야 한다는 뜻이다.
* Collector 인터페이스(인터페이스라서 원래 직접 다 구현해야 함) *
Stream의 요소인 T를 A를 누적한다(reduce와 동일) 그 다음 R로 변환해서 반환!
supplier( ) | 작업 결과를 저장할 공간을 제공 |
accumulator( ) | 스트림의 요소를 수집(collect)할 방법을 제공 (이전 연산결과와 스트림의 요소에 수행할 연산) |
combiner( ) | 두 저장공간을 병합할 방법을 제공(병렬 스트림) |
finisher( ) | 결과를 최종적으로 변환할 방법을 제공 |
* Collectors 클래스(Collect 인터페이스를 구현해 놓아서 직접 구현할 필요 없다.) *
(1) 스트림을 컬렉션, 배열로 변환
1. 스트림을 컬렉션으로 변환 – toList( ), toSet( ), toMap( ), toCollection( ) -> 모두 Collectors가 제공
-> Stream이 Student인 stuStream에서 학생들 이름만 뽑아서 Stream<String>으로 만든 뒤 List<String>에 담은 것(학생 이름이 담긴 List생성!
-> List나 Set이 아닌 특정 컬렉션을 지정하려면 toCollection( )에 해당컬렉션의 생성자 참조를 매개변수로 넣어주면 됨 (여기서는 반환결과가 ArrayList로!)
-> map으로 지정하려면 toMap을 사용하면 된다.
-> map은 key와 value를 저장해줘야 함(객체의 어떤 필드를 키로 사용할 지와 값으로 사용할지를 지정해줘야 함)
-> 요소의 타입이 Person인 스트림에서 key는 getRegId(): 주민번호, value는 객체 자기자신
(2) 스트림을 배열로 변환 - toArray( )
- 스트림에 저장된 요소들을 ‘T[]’타입의 배열로 변환
-> 매개변수가 없을 때는 Object[ ]로 반환, 해당 타입의 생성자 참조를 매개변수로 지정해줘야 함
-> 람다식으로 변환하면?
Student[ ] stuNames = studentStream.toArray( ( i ) -> new Strudent[ i ] );
(3) 스트림의 통계
1. 스트림의 통계정보 제공 - counting( ), summingInt( ), maxBy( ), minBy( ), ….
- 스트림의 요소가 몇 개인지 알 수 있다.
- 전체 counting이 아닌 그룹별로 counting하기 위해서 collect를 사용
- Collectors의 static메서드를 호출하면 Collectors.를 생략 가능. (import static java.util.stream.Collectors.counting;)
- counting이외에도 다른 메서드를 호출하는 것도 가능
- 학생스트림인 stuStream에서 총점을 다 더함
- 전체 총합이 아닌 그룹별로 합계를 구하기 위해서 collect를 사용
- import static java.util.stream.Collectors.summingInt;
- 결과가 없을 수도 있으니까(null일 수도 있어서) OptionalInt<T>
- 비교기준은 총점(Student의 getTotalScore)
- 전체 요소중에서 최대값이 아닌 그룹별로 최대값을 구하기 위해서 collect를 사용
(4) 스트림을 리듀싱 reducing( )
reduce( ) | Collectors.reducing( ) | 둘 다 거의 동일 |
전체에 대한 reducing | 그룹별로 나눠서 reducing 가능 |
- IntStream에는 매개변수 3개짜리 collect()만 정의되어 있어서 boxed()를 통해 IntStream을 Stream<Integer>로 변환해야 매개변수 1개짜리 collect( )를 사용할 수 있다.
(5) 문자열의 결합 - joining( )
- 문자열 스트림의 모든 요소를 하나의 문자열로 연결해서 반환한다.
- 스트림의 요소가 문자열이 아닌 경우에는 map( )을 먼저 이용해서 스트림의 요소를 문자열로 변환해야 한다.
- 만약 map( )없이 스트림에 바로 joining( )하면, 스트림의 요소에 toString( )을 호출한 결과를 결합
예제)
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.*;
public class Ex14_10 {
public static void main(String[] args) {
Student2[] stuArr = {
new Student2("이자바", 3, 300),
new Student2("김자바", 1, 200),
new Student2("안자바", 2, 100),
new Student2("박자바", 2, 150),
new Student2("소자바", 1, 200),
new Student2("나자바", 3, 290),
new Student2("감자바", 3, 180)
};
// 학생 이름만 뽑아서 List<String>에 저장
List<String> names = Stream.of(stuArr).map(Student2::getName).collect(Collectors.toList());
System.out.println(names);
// 학생 이름만뽑아서 joining으로 연결
String stuNames = Stream.of(stuArr).map(Student2::getName).collect(joining(" ","<",">"));
System.out.println(stuNames);
// 스트림을 배열로 변환
Student2[] stuArr2 = Stream.of(stuArr).toArray(Student2[]::new);
for(Student2 s:stuArr2)
System.out.println(s);
// 스트링을 Map<String, Student>으로 변환 학생 이름이 key
Map<String, Student2> stuMap = Stream.of(stuArr).collect(Collectors.toMap(s->s.getName(), p->p));
for(String name: stuMap.keySet())
System.out.println(name + "->" + stuMap.get(name));
long count = Stream.of(stuArr).collect(counting()); // static java.util.stream.Collectors.*; 때문에 Collectors 생략가능
System.out.println("count = " + count);
long totalScore = Stream.of(stuArr).collect(summingInt(Student2::getTotalScore));
System.out.println("totalScore = " + totalScore);
totalScore = Stream.of(stuArr).collect(reducing(0, Student2::getTotalScore, Integer::sum));
System.out.println("totalScore = " + totalScore);
Optional<Student2> topStudent = Stream.of(stuArr).collect(maxBy(Comparator.comparingInt(Student2::getTotalScore)));
System.out.println("topStudent = " + topStudent.get());
IntSummaryStatistics stat = Stream.of(stuArr).collect(summarizingInt(Student2::getTotalScore));
System.out.println(stat);
}
}
class Student2 implements Comparable<Student2> {
String name;
int ban;
int totalScore;
Student2(String name, int ban, int totalScore) {
this.name = name;
this.ban = ban;
this.totalScore = totalScore;
}
public String toString() {
return String.format("[%s, %d, %d]", name, ban, totalScore).toString();
}
String getName() { return name; }
int getBan() { return ban; }
int getTotalScore() { return totalScore; }
public int compareTo(Student2 s) {
return s.totalScore - this.totalScore;
}
}
(1) [이자바, 김자바, 안자바, 박자바, 소자바, 나자바, 감자바]
(2) <이자바 김자바 안자바 박자바 소자바 나자바 감자바>
(3)
[이자바, 3, 300]
[김자바, 1, 200]
[안자바, 2, 100]
[박자바, 2, 150]
[소자바, 1, 200]
[나자바, 3, 290]
[감자바, 3, 180]
(4)
안자바->[안자바, 2, 100]
김자바->[김자바, 1, 200]
박자바->[박자바, 2, 150]
나자바->[나자바, 3, 290]
감자바->[감자바, 3, 180]
이자바->[이자바, 3, 300]
소자바->[소자바, 1, 200]
(5) count = 7
(6) totalScore = 1420
(7) totalScore = 1420
(8) topStudent = [이자바, 3, 300]
(9) IntSummaryStatistics{count=7, sum=1420, min=100, average=202.857143, max=300}
-> Collectors가 생략될 수 있는 건 import static java.util.stream.Collectors.*;를 해줬기 때문이다.
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석11-6 스트림의 그룹화와 분할(groupingBy, partitioningBy) (0) | 2022.02.02 |
---|---|
자바의 정석11-4 스트림의 연산(중간연산과 Optioanl<T>) (0) | 2022.01.16 |
자바의 정석11-3 스트림(Stream), 스트림 생성 (0) | 2022.01.16 |
자바의 정석11-2 java.util.Function 패키지의 함수형 인터페이스와 메서드 참조 (0) | 2022.01.16 |
자바의 정석11-1 람다와 함수형인터페이스 (0) | 2022.01.16 |