collect( )

  • collect( )는 스트림의 요소를 수집하는 최종 연산이다.
  • collect( )가 스트림의 요소를 수집하려면, 어떻게 수집할 것인지 방법이 정의되어 있어야 한다.
    - collect( )가 스트림의 요소를 수집하는 방법을 정의한 것이 collector(컬렉터) 이다.
  • 컬렉터는 Collector 인터페이스를 구현한 것으로, 직접 구현하거나 미리 작성된 것을 사용할 수 있다.
  • Collectors 클래스는 미리 작성된 컬렉터를 반환하는 static 메서드들을 가지고 있다.

💡 주의 : Collector는 인터페이스이고, Collectors는 클래스이다.

정리 !

  • collect( ) : 스트림의 최종연산이며 매개변수로 컬렉터를 필요로 한다.
  • Collector : 인터페이스이며 컬렉터는 이 인터페이스를 구현해야한다.
  • Collectors : 클래스이며, static 메서드를 가지고 있고, 미리 작성된 컬렉터를 제공하는 클래스이다.
// collect() 메서드의 선언부

// 매개변수가 1개인 collect()
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로 필요로한다.

// 매개변수가 3개인 collect()
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

Stream을 컬렉션으로 변환

  • 스트림의 모든 요소를 컬렉션에 수집해서 반환할 수 있다.
  • toList( ), toSet( ), toMap( )을 이용해, 컬렉션으로 반환 받을 수 있다.
  • List와 Set과 인터페이스 대신, 특정 컬렉션을 지정할 수 있다.
    - toCollection( )안에 해당 컬렉션의 생성자 참조를 매개변수로 넣어주면 된다.
// Stream을 List로 변환해서 반환 받는 예제
List<String> names = stuStream.map(Student::getName)
						.collect(Collectors.toList());

// Stream을 특정 컬렉션으로 반환 받는 예제(Stream -> ArrayList)
ArrayList<String> list = names.stream()
							.collect(Collectors.toCollection(ArrayList::new));
                            
// Stream을 Map으로 반환 받는 예제
Map<String, Person> map = personStream.
							.collect(Collectors.toMap(p->p.getRegId(), p->p));

💡 Map의 경우 키와 값의 쌍으로 저장해야하기 때문에 객체의 어떤 필드를 키와 값으로 사용할지 지정해야 한다.

Stream을 배열로 변환

  • Stream에 저장된 요소들을 T[ ]타입의 배열로 변환하려면 , toArray( )를 사용하면 된다.
// Stream을 배열로 변환하는 예제 - 1
Student[] stuNames = studentStream.toArray(Student::new); 		// OK

// Stream을 배열로 변환하는 예제 - 2
Student[] stuNames = studentStream.toArray(); 	// Object를 반환하므로 형변환을 해주어야 한다(에러)
Student[] stuNames = (Student[])studentStream.toArray(); 		// OK

// Stream을 배열로 반환하는 예제 - 3
Object[] stuNames = studentStream.toArray();					// OK (Object를 반환한다.)

collect의 메서드를 이용한 통계구하기

  • collect( )를 사용하여, 통계를 구할 수 있다(counting, summingInt, averageInt, maxBy, minBy)
  • groupingBy( )와 함께 사용하면 그룹별 통계를 구할 수 있어 유용하다.
// 개수 구하기 - counting()
long count = stuStream.collect(Collectors.counting());

// 최대값 구하기 - max()
Optional<Student> topStudent = stuStream.
					collect(Collectors.maxBy(Comparator.comparingInt(Student::getTotalScore)));

// 합계 구하기 - summingInt()
long totalScore = stuSTream.collect(Collectors.summingInt(Student::getTotalScore));

// IntSummaryStatistics 반환 받기 - summarizingInt()
IntSummaryStatistics stat = stuStream.
								.collect(Collectors.summarizingInt(Student::getTotalScore));
                                

reducing( ) - 리듀싱

  • collect( )로 리듀싱을 할 수 있다.
  • IntStream에는 매개변수 3개짜리 collect( )만 정의되어 있으므로, boxed( )를 통해 Stream<Integer>로 변환을 거쳐야 매개변수 1개짜리 collect( )가 사용가능하다.
// IntStream으로 collect()를 이용한 리듀싱 예제 1 - 최대 값 구하기
Optional<Integer> max = intStream.boxed().collect(Collectors.reducing(Integer::max));

// IntStream으로 collect()를 이용한 리듀싱 예제 2 - 합계 구하기
long sum = intStream.boxed().collect(Collectors.reducing(0, (a,b) -> a + b));

// Stream으로 collect()를 이용한 리듀싱 예제 3 - 합계 구하기
int grandTotal = stuStream.collect(Collectors.reducing(0, Student::getTotalScore, Integer::sum));

joining( ) - 문자열 결합

  • 문자열 스트림의 모든 요소를 하나의 문자열로 결합해서 반환한다.
  • 구분자를 지정해 줄 수 있고, 접두사(prefix)와 접미사(suffix)를 지정 가능하다.
  • 스트림의 요소가 String이나 StringBuffer와 같이 CharSequence의 자손인 경우에만 결합이 가능하다.
  • 스트림의 요소가 문자열이 아닌경우, 먼저 map( )을 통해 변환해야 결합이 가능하다.
// joining()을 사용한 일반 결합
String studentNames = stuStream.map(Student::getName).collect(joining());

// joining()을 사용한 구분자 지정 결합
String studentNames = stuStream.map(Student::getName).collect(joining(","));

// joining()을 사용한 구분자, 접두사, 접미사 지정 결합
String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));

그룹화와 분할

  • 그룹화란 특정 기준으로 스트림의 요소를 그룹화하는 것을 의미한다.
  • groupingBy( )는 스트림을 n개의 그룹으로 분할하고, partioningBy( )는 2개의 그룹으로 분할한다.
  • groupingBy( )는 Function으로 , partitioningBy( )는 Perdicate로 스트림의 요소를 분류한다.

partitioningBy( )에 의한 분류


// Collectors는 static import 되었다고 가정하고 Collectors를 생략한다.

// 학생들을 성별로 나누어 List에 담는 예제 -  단순 분할
Map<Boolean, List<Student>> stuBySex = stuStream
										.collect(
                                        partitioningBy(Student::isMale)); // 학생들을 성별로 분할

List<Student> maleStudent = stuBySex.get(true);		// Map에서 남학생 목록을 가져온다.
List<Student> femaleStudent = stuBySex.get(false);		// Map에서 여학생 목록을 가져온다.

// counting()을 사용한 학생 수 구하기 예제
Map<Boolean, Long> stuNumBySex = stuStream
						.collect(partitioningBy(Student::isMale, counting()));
                        
// maxBy를 사용한 남학생 1등과 여학생 1등 구하기 예제
Map<Boolean, Optional<Student>> topScoreBySex = stuStream
				.collect(partitioningBy(Student::isMale,
                			maxBy(comparingInt(Student::getScore))
                            )
                         );

💡maxBy( )는 반환타입이 Optional<T>이다. Optional대신 객체로 반환받으려면, collectingAndThen( )과
    Optional :: get을 함께 사용하면 된다.

// Optional::get 사용예제

// Collectors는 static import 되었다고 가정하고 Collectors를 생략한다.

Map<Boolean, Student> topScoreBySex = stuStream
	.collect(
    	partitioningBy(Student::isMale,
        	collectingAndThen(
            	maxBy(comparingInt(Student::getScore)), Optional::get
                )
            )
    );
    
// 성적이 150점 아래인 학생들을 불합격처리 하는 예제 - 이중 분할
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex = stuStream
	.collect(
    	partitioningBy(Student::isMale,
        	partitioningBy(s -> s.getScore() < 150)
        )
    );

출처 : 자바의 정석 3rd Edition(남궁성 저, 도우출판)

0개의 댓글