Collector 인터페이스
Collector 인터페이스를 상속해서 커스텀 컬렉션을 만들 수 있다. 다음은 Collector 인터페이스이다.
1 2 3 4 5 6 7
| public interface Collector<T, A, R> { Supplier<A> supplier(); BiConsumer<A, T> accumulator(); Function<A, R> finisher(); BinaryOperator<A> combiner(); Set<Characteristics> characteristics(); }
|
- supplier : 결과를 위한 컨테이너 생성
- accumulator : 결과 컨데이너에 요소 추가
- finisher : 최종 변환값을 결과 컨테이너로 적용
- combiner : 병렬로 처리될 때 서로 다른 두 결과 컨테이너 병합
- characteristics : 컬렉터의 연산에 대한 힌트 제공
- UNORDERED : 리듀싱 결과는 스트림 요소의 방문 순서나 누적 순서에 영향을 받지 않음
- CONCURRENT : 다중 스레드에서 accumulator를 동시에 호출 가능
- IDENTITY_FINISH : 리듀싱 과정의 최종 결과로 누적자 객체를 바로 사용 가능
가령 다음과 같은 클래스가 있다고 하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Student { private String name; private Set<Subject> subjects;
public Student(String name, Set<Subject> subjects) { this.name = name; this.subjects = subjects; }
public String getName() { return name; }
public Set<Subject> getSubjects() { return subjects; } }
public enum Subject { MATH, ENGLISH, MUSIC, SCIENCE }
|
학생들의 목록을 가지고 수업별로 수강하는 학생 이름 리스트를 만들고자 한다. 즉 List<Student>
를 Map<Subject, List<String>>
으로 만들고자 한다.
커스텀 컬렉션을 만드는 Collector를 사용하면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class StudentSubjectCollector implements Collector<Student, Map<Subject, List<String>>, Map<Subject, List<String>>> { @Override public Supplier<Map<Subject, List<String>>> supplier() { return () -> new HashMap<>() {{ put(Subject.MATH, new ArrayList<>()); put(Subject.ENGLISH, new ArrayList<>()); put(Subject.SCIENCE, new ArrayList<>()); put(Subject.MUSIC, new ArrayList<>()); }}; }
@Override public BiConsumer<Map<Subject, List<String>>, Student> accumulator() { return (Map<Subject, List<String>> result, Student student) -> { for(Subject subject : student.getSubjects()) { result.get(subject).add(student.getName()); } }; }
@Override public BinaryOperator<Map<Subject, List<String>>> combiner() { return (Map<Subject, List<String>> map1, Map<Subject, List<String>> map2) -> { for(Subject subject : map2.keySet()) { map1.get(subject).addAll(map2.get(subject)); }
return map1; }; }
@Override public Function<Map<Subject, List<String>>, Map<Subject, List<String>>> finisher() { return Function.identity(); }
@Override public Set<Characteristics> characteristics() { return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); } }
|
1 2 3 4 5 6 7 8 9 10 11
| List<Student> students = Arrays.asList( new Student("Kim", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MATH))), new Student("Park", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MUSIC))), new Student("Lee", new HashSet<>(Arrays.asList(Subject.SCIENCE, Subject.MATH))), new Student("Choi", new HashSet<>(Arrays.asList(Subject.MUSIC, Subject.MATH))), new Student("Song", new HashSet<>(Arrays.asList(Subject.SCIENCE, Subject.MUSIC))), new Student("Kang", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MATH, Subject.MUSIC, Subject.SCIENCE))) );
Map<Subject, List<String>> result = students.stream().collect(new StudentSubjectCollector()); Map<Subject, List<String>> parallelResult = students.parallelStream().collect(new StudentSubjectCollector());
|
다음은 이와 같은 동작을 하는 코드이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| List<Student> students = Arrays.asList( new Student("Kim", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MATH))), new Student("Park", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MUSIC))), new Student("Lee", new HashSet<>(Arrays.asList(Subject.SCIENCE, Subject.MATH))), new Student("Choi", new HashSet<>(Arrays.asList(Subject.MUSIC, Subject.MATH))), new Student("Song", new HashSet<>(Arrays.asList(Subject.SCIENCE, Subject.MUSIC))), new Student("Kang", new HashSet<>(Arrays.asList(Subject.ENGLISH, Subject.MATH, Subject.MUSIC, Subject.SCIENCE))) );
Map<Subject, List<String>> res = students.stream() .map(student -> { Map<Subject, String> studentSubjects = new HashMap<>(); for (Subject subject : student.getSubjects()) { studentSubjects.put(subject, student.getName()); }
return studentSubjects; }) .flatMap(map -> map.entrySet().stream()) .collect(groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|