업무하면서 람다와 Stream 때문에 계속 헤매서 일단 알게된 대로 정리해보려고 한다.
Stream의 최종 처리 단계에서 사용되는 Java Collector
Stream.collect()
는 데이터의 중간 처리 후 마지막에 원하는 형태로 변환해주는 역할을 한다.
모든 Stream의 요소를 List 인스턴스로 수집하는 데 사용할 수 있다.
해당 메서드는 특정한 List를 구현하는 것이 아님
stream().collect(Collectors.toList())
Collectors의 groupingBy() 또는 groupingByConcurrent()가 리턴하는 Collector을 매개값으로 대입하여 사용할 수 있다.
groupingBy()
: Map 객체 리턴groupingByConcurrent
: ConcurrentMap 객체 리턴Collectors.groupingBy()
groupingBy collector는 일부 속성 별로 객체를 grouping하고 결과를 Map 인스턴스에 저장하는 데 사용된다.
1. groupingBy(Function<T, K> classifier)
Function<T, K> classifier
Collector<T, ?, Map<K, List<T>>>
T를 K로 매핑한 후,
키가 K이면서 T를 저장하는 요소를 값으로 갖는 Map 생성
2. groupingBy(Function<T, K> classifier, Collector<T, A, D> downstream)
groupingBy(Function<T, K> classifier, Collector<T, A, D> downstream)
매개변수로 Function<T, K> classifier, Collector<T, A, D> collector을 사용하는 groupingBy 메소드
Function<T, K> classifier, Collector<T, A, D> collector
Collector<T, ?, Map<K, D>>
T를 K로 매핑한 후,
키가 K이면서 키에 저장된 D객체에 T를 누적한 Map 생성
3. groupingBy(Function<T, K> classifier, Supplier<Map<K, D>> mapFactory, Collector<T, A, D> downstream>
List<Student> studentList = Arrays.asList(
new Student("이산", 10, Student.Sex.MALE, Student.City.Seoul),
new Student("진영", 8, Student.Sex.FEMALE, Student.City.Pusan),
new Student("별찬", 9, Student.Sex.MALE, Student.City.Seoul),
new Student("민주", 11, Student.Sex.FEMALE, Student.City.Pusan)
);
1. groupingBy(Function<T, K> classifier)
Map<Student.Sex, List<Student>> studentList.stream()
.collect(Collectors.groupingBy(Student::getSex));
2. groupingBy(Function<T, K> classifier, Collector<T, A, D> downstream)
Map<Student.City, List<String>> mapByCity = studentList.stream()
.collect(Collectors.groupingBy(
Student::getCity,
Collectors.mapping(Student::getName, Collectors.toList()))
);
Function<Student, Student.City> classifier = Student::getCity;
Map의 Key를 얻어내기 위해 T를 K로 매핑
여기서 T는 Student이고, K는 Student.City가 될 것
// Map의 Value에 해당하는 D를 얻는 과정
결과적으로, 우리는 Value가 List<Student>가 아니라 List<String>을 얻어야 함
여기서 String은 학생의 이름을 의미
Function<Student, String> mapper = Student::getName;
Student를 Student::getName으로 매핑
Collector<String, ?, List<String>> collector1 = Collectors.toList();
Collector<Student, ?, List<String>> collector2 = Collectors.mapping(mapper, collector1);
Student::getName을 다시 List<String>으로 매핑
이 떄 사용하는 Collectors.mapping() 는 T를 U로 매핑한 후, U를 R에 수집하는 역할
Collector<Student, ?, Map<Student.City, List<String>>> collector3 =
Collectors.groupingBy(classifier, collector2);
Map<Student.City, List<String>> mapByCity = studentList.stream().collect(collector3);
3. groupingBy(Function<T, K> classifier, Supplier<Map<K, D>> mapFactory, Collector<T, A, D> downstream>
TreeMap<Student.City, List<String>> treeMapByCity = studentList.stream()
.collect(Collectors.groupingBy(
Student::getCity,
TreeMap::new,
Collectors.mapping(Student::getName, Collectors.toList()))
);
Supplier가 들어갈 자리에 원하는 Map의 형태를 지정해주는 것
Collector
을 가질 수 있다.✔ 매개 변수가 2개 이상 있는 groupingBy() 메소드가 그룹핑 후 매핑 및 집계를 사용한다. 이를 통해 value 값을 무궁무진하게 변화시킬 수 있다.
mapping(Function<T, U> mapper, Collector<U, A, R> collector>
joining(CharSequence delimiter)
예제
Map<Student.Sex, String> mapByName = studentList.stream()
.collect(Collectors.groupingBy(
Student::getSex,
Collectors.mapping(
Student::getName,
Collectors.joining(", ")
)
)
);
학생들을 성별로 그룹핑한 다음,
같은 그룹에 속한 학생 이름을 쉼표로 구분해서 문자열을 만들고
성별을 Key로, 문자열을 Value로 갖는 Map을 생성
mapping() 메소드를 이용해 Student를 String으로 매핑
Student -> String(구분자 없음) -> String(구분자 있음)
으로 매핑
구분자는 맨 마지막에는 첨가되지 않는다는 것을 유의