업무하면서 람다와 Stream 때문에 계속 헤매서 일단 알게된 대로 정리해보려고 한다.

Stream의 최종 처리 단계에서 사용되는 Java Collector
Stream.collect()는 데이터의 중간 처리 후 마지막에 원하는 형태로 변환해주는 역할을 한다.

stream().collect()

  • stream 요소들을 List, Set, Map 자료형으로 변환
  • stream 요소들의 결합(joining)
  • stream 요소들의 통계(최대, 최소, 평균값 등)
  • stream 요소들의 그룹화와 분할

Collectors.toList()

모든 Stream의 요소를 List 인스턴스로 수집하는 데 사용할 수 있다.
해당 메서드는 특정한 List를 구현하는 것이 아님

stream().collect(Collectors.toList())

요소를 그룹핑해서 수집

Collectors.groupingBy()

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>

  • 두 번째의 매개변수에서 Supplier가 추가된 형태
  • 그냥 Map이 아니라, TreeMap 같은 Supplier가 제공하는 Map 사용

예시

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));
  • Student의 Sex를 기준으로 grouping
  • Function<T, K>를 매개변수로 사용 -> T를 K로 매핑
    이 때 T는 Student, K는 Student.Sex이므로, Map<Student.Sex(K), List<Student(T)>> 이 생성되는 것

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()))
   	);
  • Student의 City를 기준으로 grouping
  • 매개변수 : Function<T, K> 와 Collector<T, A, D> collector
Function<Student, Student.City> classifier = Student::getCity;
MapKey를 얻어내기 위해 TK로 매핑
여기서 TStudent이고, KStudent.City가 될 것

// Map의 Value에 해당하는 D를 얻는 과정
결과적으로, 우리는 ValueList<Student>가 아니라 List<String>을 얻어야 함
여기서 String은 학생의 이름을 의미

Function<Student, String> mapper = Student::getName;
StudentStudent::getName으로 매핑

Collector<String, ?, List<String>> collector1 = Collectors.toList();
Collector<Student, ?, List<String>> collector2 = Collectors.mapping(mapper, collector1);
Student::getName을 다시 List<String>으로 매핑
이 떄 사용하는 Collectors.mapping()TU로 매핑한 후, UR에 수집하는 역할

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>

  • 2번의 형태와 원리는 거의 유사하나, Map의 형태를 구체화하여 저장할 수 있음
TreeMap<Student.City, List<String>> treeMapByCity = studentList.stream()
	.collect(Collectors.groupingBy(
		Student::getCity,
		TreeMap::new,
		Collectors.mapping(Student::getName, Collectors.toList()))
);

Supplier가 들어갈 자리에 원하는 Map의 형태를 지정해주는 것

그룹핑 후 매핑 및 집계

  • Collectors.groupingBy() 메소드는 그룹핑 후 매핑이나 집계를 할 수 있도록 두 번째 매개값으로 Collector을 가질 수 있다.

매개 변수가 2개 이상 있는 groupingBy() 메소드가 그룹핑 후 매핑 및 집계를 사용한다. 이를 통해 value 값을 무궁무진하게 변화시킬 수 있다.

mapping(Function<T, U> mapper, Collector<U, A, R> collector>

  • 매개 변수 : Function<T, U> mapper, Collector<U, A, R> collector>
  • 리턴 타입 : Collector<T, ?, R>
    T를 U로 매핑한 후, U를 R에 수집

joining(CharSequence delimiter)

  • 매개 변수 : CharSequence delimeter
  • 리턴 타입 : Collector<CharSequence, ?, String>
    CharSequence를 구분자로 연결한 String을 산출

예제

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(구분자 있음)으로 매핑
구분자는 맨 마지막에는 첨가되지 않는다는 것을 유의


https://steady-coding.tistory.com/318

0개의 댓글

Powered by GraphCDN, the GraphQL CDN