반응형

collect()와 Collectors

  • collect()는 Collector를 매개변수로 하는 스트림의 최종연산

collect()는 Collector라는 인터페이스를 매개변수로 하는 스트림의 최종 연산이다.

reduce()와 collect()는 둘다 최종연산이다.
이 둘의 차이에 대해 궁굼할 수 있다.
reduce()는 리듀싱, collect()는 그룹별리듀싱 이러한 차이가 있다.
reduce의 리듀싱은 그냥 전체에 대한 리듀싱이고,
collect()는 그룹별로 나눠서 리듀싱이 가능하다.

스트림 요소 전체에 대해 리듀싱을 할 때는 reduce()를 사용하면 되고,
스트림 요소를 그룹별로 나눠서 리듀싱 할 때는 collect()를 사용하면 된다.

collect()는 2개가 있다.
collect(Collector collecotr),
collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) 인데, 두번째 것은 잘 쓰이지 않는다.



  • Collector는 수집(collect)에 필요한 메서드를 정의해 놓은

Collector<T, A, R>는 인터페이스인데, 스트림의 요소 T를 A에 누적한다음, 결과를 R로 변환해서 반환한다.

누적해서 저장할 곳을 제공하는 것이 supplier()이고,

accumulator()는 누적방법이고,

combiner()는 병렬 작업을 했을 때, 각각의 쓰레드가 작업한 것들을 합치는 것이다. 합칠 때 어떻게 합칠 것인가를 지정해 주는 것이 combiner이다.

finisher()는 최종적으로 어떤 스트림으로 줄 것인가를 지정하는 것이다. 변환이 필요없으면 안해도 된다.

앞서 reduce()의 핵심은 identity(초기값)과 accumulator(누적수행할 작업) 이었다.
마찬가지로 Collector의 핵심도 supplier와 accumulator 이다.

characteristics()는 위의 작업을 수행하는데 특성을 지정해줄 수 있다. 

이러한 것들을 구현해서 매개변수로 collect(Collector collector)에 매개변수로 넣어줘야 한다.
이것을 직접 전부 구현해야 할까?

다행히도 Collectors 클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공한다.

 

  • Collectors 클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공

원래는 Collector인터페이스를 직접 구현해야 한다.

그러나, Collectors라는 클래스가 Collector인터페이스를 구현해놓았다.
그래서 우리가 직접 구현할 필요가 없다.
우리는 Collectors 클래스를 가져다가 사용하면 된다.

 

정리하면 아래와 같다.

 

 


 

스트림을 컬렉션,  배열로 변환

  • 스트림을 컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()

스트림을 컬렉션으로 변환할 때 사용되는 위의 메서드들은 Collectors 클래스가 제공한다.
그래서 Collectors.toList() 이런식으로 사용하면 된다.

StuStream에서 학생들 이름만 뽑아서 그것을 리스트에 담는 것은, 
Stream<Student>에서 이름만 뽑아서 Stream<String>으로 만든 후, List<String>에 저장하는 것이다.
이러한 경우에 .collect(Collectors.toList())를 사용한다.

그런데 만약에 반환타입을 리스트를 구현한 특정 클래스를 지정하고 싶다면,
.collect(Collectors.toCollection(ArrayList::new)) 이런식으로 하면된다.
ArrayList로 지정했으므로, 반환결과가 ArrayList가 된다.

Map에 담을 수도 있다.
.collect(Collectors.toMap(p -> p.getRegId(), p -> p))
map은 key와 value로 저장해야 한다.
그래서 p라는 객체가 있으면 p라는 객체의 주민번호를 key로 하고, value는 p객체 자신을 그대로 저장하도록 했다.

 

  • 스트림을 배열로 변환 - toArray()

스트림을 배열로 변환할 때는 toArray()를 사용한다.
이것은 stream에 있는 메서드다.
그런데 이 메서드는 2가지가 있다.
매개변수 없는 메서드와 없는 메서드가 있는데, 

매개변수가 없는 것은 Object[]로 반환한다.

그래서 studentStream을 Student[]에 담으려면 매개변수 있는 .toArray(Student[]::new)를 사용해야 한다.
반환타입과 매개변수가 일치해야 한다. 람다식으로 바꿔서 쓰면, .toArray((i) -> new Student[i]) 으로 쓸 수 있다.

 

  • 스트림의 통계 - counting(), summingInt(),maxBy(), minBy(), ...

스트림에 있는 stuStream.count()를 호출하면, 스트림의 요소가 몇개인지 알 수 있었다.

collect()를 이용해서도 똑같은 것을 할 수 있다.
stuStream.collect(counting()) 이렇게 사용하면 된다. 사실 이것은 Collectors.이 생략된 것이다.

import static java.util.stream.collectors.*; 이렇게 하면,
static메서드들에서 Collectors가 생략 가능하다.

그렇다면, 그냥 stream의 .count()를 사용하면 될텐데 왜 이렇게 나눠 놓은 것일까?

collect(Collectors.counting())을 사용하면, .count()와 달리,
앞서 collect의 특징인 그룹별로 할 수 있다.
즉, 그룹별로 counting()이 된다. 지금은 그룹으로 나누지 않아서 위의 코드 두개다 전체 카운팅이 되버린다.

count()는 항상 전체 카운팅만된다. 즉, 스트림의 모든 요소를 카운팅하는데, 
collect(Collectors.counting()은 그룹별로 나눠서 카운팅 하는 것이 가능하다.

 

그리고 .sum()과 .collect(summingInt())도 마찬가지이다.
stuStream에서 학생총점을 다 더한다.

sum()은 항상 전체 sum만 가능한데,
.collect(summingInt())는 그룹별 sum이 가능하다. 

 

max()와 .collect(maxby())도 마찬가지이다.

총점이 가장 높은 것을 얻어서 topScore에 담는 것이다.
이 결과가 없을 수도 있으므로 Optional로 반환을 한다.
.collect(maxBy())를 사용할 때는 비교기준과 총점을 주면 총점의 최대값을 얻을 수 있다.

max()는 전체에서 1등만 구할 수 있지만,
.collect(maxBy())는 그룹을 남자 여자로 나누고, 남자 1등, 여자 1등을 구할 수 있다. 

 


스트림을 리듀싱 - reducing()

  • 스트림을 리듀싱 - reducing()

Collectors가 제공하는 reducing()이라는 메서드가 있다.

하는 일은 reduce()와 같다.

그렇다면, Collectors의 reducing()은 어떤 차이가 있냐면,
reduce()는 전체 리듀싱 이라면, Collectors의 reducing을 그룹별 리듀싱이 가능하다.

리듀싱이라는 것은 sum()이나 count() 등을 전부 배웠는데, 
reduce()는 전체sum()이나 전체count()를 구할 수 있지만,

Collectors의 reducing()을 이용하면, 그룹별 sum(), 그룹별 count() 이러한 것이 가능해진다.

reduce()는 전체 리듀싱,
.collect(reducing())은 그룹별 리듀싱이 가능하다.

max, sum 등을 구할 때 reduce를 사용했던 것 처럼, .collect(reducing()을 이용해서 만들 수 있다.

 

  • 문자열 스트림의 요소를 모두 연결 - joining()

joining() Collectors가 가지고 있는 메서드다. Collectors.joining() 이다.
이 메서드가 반환하는 것도 collector이다.
그래서 학생 이름을 모두 뽑아서 하나의 문자열로 갈아타고 싶다면,
그럴 때에는 Stream<Student> -> Stream<String> 으로 만든다. 이때 쓰는게 getName이다.
그러면 하나의 문자열로 붙어서 온다. joining(",")이렇게 콤마를 주게되면, 이름이 구분되어 저장된다.

반응형

'JAVA' 카테고리의 다른 글

[JAVA] JVM 동작원리 및 기본개념(JVM Internal)  (0) 2022.12.07
스트림의 그룹화와 분할  (0) 2022.12.02
스트림의 최종연산  (0) 2022.11.25
Optional<T>  (0) 2022.11.22
스트림의 중간연산(2)  (0) 2022.11.19