collect()와 Collector, Collectors
- collect()는 Collector(인터페이스)를 매개변수로 하는 스트림의 최종연산이다.
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) // 잘 안쓰임
reduce()와 collect()은 reduce()이 스트림 요소 전체에 대한 리듀싱을 한다고 하면 collect()는 그룹별 리듀싱이 가능하다는 차이가 있다. 이처럼 스트림을 나눠놓고 작업을 할수 있다는 것이 collect()의 장점이다.
- Collector 인터페이스는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스를 말한다.
public interface Collector<T, A, R> { // T(요소)를 A에 누적한 다음, 결과를 R로 변환해서 반환
Supplier<A> supplier(); // StringBuilder::new 누적할 곳
BiConsumer<A, T> accumulator(); // (sb, s) -> sb.append(s) 누적방법
BinaryOperator<A> combiner(); // (sb1, sb2) -> sb1.append(sb2) 결합방법(병렬)
Function<A, R> finisher(); // sb -> sb.toString() 최종변환
Set<Characteristics> characteristics(); // 컬렉터의 특성이 담긴 Set을 반환
...
}
- Collectors클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공한다.
변환 – mapping(), toList(), toSet(), toMap(), toCollection(), …
통계 – counting(), summingInt(), averagingInt(), maxBy(), minBy(), summarizingInt(), …
문자열 결합 – joining()
리듀싱 – reducing()
그룹화와 분할 – groupingBy(), partitioningBy(), collectingAndThen()
스트림을 컬렉션로 변환
- 스트림을 컬렉션으로 변환 – toList( ), toSet( ), toMap( ), toCollection( )
List<String> names = stuStream.map(Student::getName) // Stream<Student>→Stream<String>
.collect(Collectors.toList()); // Stream<String>→List<String>
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new)); // Stream<String>→ArrayList<String>
Map<String,Person> map = personStream
.collect(Collectors.toMap(p->p.getRegId(), p->p));// Stream<Person>→Map<String,Person>
스트림의 통계
- 스트림의 통계정보 제공 – counting( ), summingInt( ), maxBy( ), minBy( )
long count = stuStream.count(); // Stream.count()
long count = stuStream.collect(counting()); // Collectors.counting()
Stream의 count() 메서드는 스트림의 모든 요소의 요소의 개수 반환한다. Collectors의 메서드 counting()도 같은 동작을 하지만 굳이 Collectors의 메서드를 사용하는 이유는 그룹별로 카운팅 가능하기 때문이다.
long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum()
long totalScore = stuStream.collect(summingInt(Student::getTotalScore));
- summingInt()도 마찬가지로 그룹별 합계를 구할 수 있기 때문에 사용된다.
OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();
Optional<Student> topStudent = stuStream
.max(Comparator.comparingInt(Student::getTotalScore));
Optional<Student> topStudent = stuStream
.collect(maxBy(Comparator.comparingInt(Student::getTotalScore)));
- maxBy( ), minBy( )도 마찬가지로 그룹별 최소/최대값을 구할 수 있기 때문에 사용된다.
스트림을 리듀싱
- 스트림을 리듀싱 – reducing( )
Collector reducing(BinaryOperator<T> op)
Collector reducing(T identity, BinaryOperator<T> op)
Collector reducing(U identity, Function<T,U> mapper, BinaryOperator<U> op) // map(변환작업)+reduce
Collectors의 메서드 reducing()는 Stream의 reduce() 와 같은 동작을한다. 마찬가지로 reduce()은 스트림 전체에 대한 리듀싱을, reducing()은 그룹별 리듀싱에 사용된다.
long sum = intStream.reduce(0, (a,b) -> a + b);
long sum = intStream.boxed().collect(reducing(0, (a,b)-> a + b));
int grandTotal = stuStream.map(Student::getTotalScore).reduce(0, Integer::sum);
int grandTotal = stuStream.collect(reducing(0, Student::getTotalScore, Integer::sum));
- 문자열 스트림의 요소를 모두 연결 – joining ( )
// Stream<Student> -> Stream<String>.collect(joining());
String studentNames = stuStream.map(Student::getName).collect(joining());
String studentNames = stuStream.map(Student::getName).collect(joining(",")); // 구분자
String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));
String studentInfo = stuStream.collect(joining(",")); // Student의 toString()으로 결합
스트림의 그룹화와 분할
Collectors의 partitioningBy( ), groupingBy( )메서드를 통해 스트림을 그룹화와 분할을 할 수 있다.
스트림의 분할
- 스트림의 요소를 2분할 – partitioningBy( )
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)
partitioningBy(Predicate predicate) 메서드는 매개변수로 기준을 받아 Key는 boolean, Value는 스트림의 요소를 가지는 List인 map으로 반환한다.
Map<Boolean, List<Student>> stuBySex = stuStream
.collect(partitioningBy(Student::isMale)); // 학생들을 성별로 분할
List<Student> maleStudent = stuBySex.get(true); // Map에서 남학생 목록을 얻는다.
List<Student> femaleStudent = stuBySex.get(false); // Map에서 여학생 목록을 얻는다.
partitioningBy(Predicate predicate, Collector downstream)은 매개변수로 기준과 Collectors의 메서드( Collector를 반환하는)를 통해 분활과 함깨 그룹별 작업을 할 수 있다.
Map<Boolean, Long> stuNumBySex = stuStream
.collect(partitioningBy(Student::isMale, counting())); // 분할 + 통계
System.out.println("남학생 수 :"+ stuNumBySex.get(true)); // 남학생 수 :8
System.out.println("여학생 수 :"+ stuNumBySex.get(false)); // 여학생 수 :10
Map<Boolean, Optional<Student>> topScoreBySex = stuStream // 분할 + 통계
.collect(partitioningBy(Student::isMale, maxBy(comparingInt(Student::getScore))));
System.out.println("남학생 1등 :"+ topScoreBySex.get(true)); // 남학생 1등 :Optional[[나자바,남, 1, 1,300]]
System.out.println("여학생 1등 :"+ topScoreBySex.get(false)); //여학생 1등 :Optional[[김지미,여, 1, 1,250]]
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex = stuStream // 다중 분할
.collect(partitioningBy(Student::isMale, // 1. 성별로 분할(남/녀)
partitioningBy(s -> s.getScore() < 150))); // 2. 성적으로 분할(불합격/합격)
List<Student> failedMaleStu = failedStuBySex.get(true).get(true);
List<Student> failedFemaleStu = failedStuBySex.get(false).get(true);
스트림의 그룹화
- 스트림의 요소를 그룹화(n분할) – groupingBy( )
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
groupingBy()는 매개변수가 partitioningBy()와 달리 Key의 값으로 true와 false로만 분할 하는 것이 아니라
Key값으로 Function의 반환 값의 종류만큼 그룹화(n분할)할 수 있다. 마찬가지로 Value는 List이다(toList()).
Map<Integer, List<Student>> stuByBan = stuStream // 학생을 반별로 그룹화
.collect(groupingBy(Student::getBan, toList())); // toList() 생략가능
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream // 다중 그룹화
.collect(groupingBy(Student::getHak, // 1. 학년별 그룹화
groupingBy(Student::getBan) // 2. 반별 그룹화
));
Map<Integer, Map<Integer, Set<Student.Level>>> stuByHakAndBan = stuStream
.collect(
groupingBy(Student::getHak, groupingBy(Student::getBan, // 다중 그룹화(학년별, 반별)
// 성적등급(Level)으로 변환. List<Student> → Set<Student.Level>
mapping(s-> {
if (s.getScore() >= 200) return Student.Level.HIGH;
else if(s.getScore() >= 100) return Student.Level.MID;
else return Student.Level.LOW;
} , toSet()) // mapping() // enum Level { HIGH, MID, LOW }
)) // groupingBy()
); // collect()
스트림의 변환
'Java' 카테고리의 다른 글
[JAVA] 부동 소수점 방식(Floating-Point Number Representation)과 정밀도 (0) | 2024.04.14 |
---|---|
[JAVA] 네트워킹(Networking) (0) | 2024.03.14 |
[JAVA] 스트림(Stream) API(3, 최종 연산) (0) | 2024.03.09 |
[JAVA] 스트림(Stream) API(2, 중간 연산, Optinal<T>) (0) | 2024.02.29 |
[JAVA] 스트림(Stream) API(1) (0) | 2024.02.25 |