개발 공부/Java & Spring

23. 스트림 - collect()

빵다희 2023. 3. 26. 22:02

💡 collect() & Collector & Collectors

collect() 스트림의 최종연산, 매개변수로 매개변수로 Collectors를 필요로 한다.
Collector 인터페이스. 컬렉터는 이 인터페이스를 구현해야한다.  
Collectors 클래스.Collector인터페이스를 구현한 것, 직접 구현할 수 도 있고 미리 작성된걸 사용할 수도 있다.
// Collecter를 구현한 클래스의 객체를 매개변수로
Object collect(Collector collect) 

//Collecter인터페이스를 구현하지 않고 간단히 람다식으로 수집할 때 사용하면 편리함.
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

 

스트림을 컬렉션과 배열로 변환 - toList(), toSet(), toMap(), toCollection(), toArray()
: 스트림의 요소를 컬렉션에 수집하려면, Collectors의 메서드를 사용하면 되고 
List나 Set이 아닌 특정 컬렉션을 지정하려면, toCollection()에 해당컬렉션의 생성자 참조를 매개변수로 넣어주면 된다.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class StreamEx6 {
    public static void main(String[] args) {
        Student[] stuArr = {
                new Student("이자바", 3, 300),
                new Student("김자바", 1, 200),
                new Student("안자바", 2, 100),
                new Student("박자바", 2, 150),
                new Student("소자바", 1, 200),
                new Student("나자바", 3, 290),
                new Student("감자바", 3, 180),
        };
        // 학생 이름만 뽑아서 list<String>에 저장
        List<String> names = System.of(stuArr).map(Student::getName)
                                              .collect(Collectors.toList());
        // 특정 컬렉션 지정하기(arrayList)
        ArrayList<String> list = names.stream()
                                      .collect(Collectors.toCollection(ArrayList::new));
        // 스트림을 배열로 변환 
        Student[] stuArr2 = System.of(stuArr).toArray(Student[]::new);
        
        // 매개변수를 지정하지 않은 배열 
        Object[] stuNames = System.of(stuArr).toArray();
        
        //스트림을 Map<String,Student>로 변환, 학생 이름이 key
        Map<String,Student> stuMap = Stream.of(stuArr).collect(Collectors.toMap(s->s.getName(),p->p));
    }
}
     Class Student implements Comparable<Student>{
        String name;
        int ban;
        int totalScore;

        Student(String name ,int ban, int totalScore){
            this.name = name;
            this.ban = ban;
            this.totalScore = totalScore;
        }
        public String toString(){
            return String.format("[%s, %d, %d]", name, ban, totalScore).toString();
        }
}

 

통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy()
: 이미 최종연산에 통계 함수들이 많이 있어서 굳이 collect를 사용할 필요는 없지만,
groupingBy()와 함께 사용될 때 유용한다.
/* 갯수 구하기 Stream.count()와 같다. */
long count = Stream.of(stuArr).collect(counting());

/* 합계 구하기 */
long totalScore = Stream.of(stuArr).collect(summingInt(Student::getTotalScore));

/* max 값 구하기 */
Optional<Student> topStudent = Stream.of(stuArr)
                .collect(maxBy(Comparator.comparingInt(Student::getTotalScore)));
                
/* summingInt와 summarizingInt를 혼동하지 않도록 주의 */                
IntSummaryStatistics stat = Stream.of(stuArr)
                .collect(summarizingInt(Student::getTotalScore));

 

리듀싱 - reducing()
: collection으로 리듀싱이 가능하지만, 주의해야할 점은 IntStream등 기본형 스트림에는 매개변수가 3개짜리인 collect()만 정의되어있으므로 매개변수 1개짜리 collect()를 사용하려면 boxed()를 통해 변환해야한다.
IntStream intStream = new Random().ints(1,46).distinct().limit(6);
/* Stream<Integer>로 변환 */
Optional<Integer> max = intStream.boxed().collect(reducing(Integer::max));

/* 변수에 초기값 설정 */
long sum = intStream.boxed().collect(reducing(0 ,(a,b) -> a + b));

/* 변수에 초기값, map, 처리함수 설정 */
int grandTotal = stuStream.collect(reducing(0, Student::getTotalScore,Integer::sum));

 

문자열 결합 - joining()
: 문자열 스트림의 모든요소를 하나의 문자열로 연결해서 반환한다.
구분자, 접두사, 접미사 지정 가능 
스트림의 요소가 문자열이 아닌경우에는 먼저 map()을 이용해서 스트림의 요소를 문자열로 변환해야한다.
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class StreamEx6 {
    public static void main(String[] args) {
        Student[] stuArr = {
                new Student("이자바", 3, 300),
                new Student("김자바", 1, 200),
                new Student("안자바", 2, 100),
                new Student("박자바", 2, 150),
                new Student("소자바", 1, 200),
                new Student("나자바", 3, 290),
                new Student("감자바", 3, 180),
        };
        String stuNames = Stream.of(stuArr).map(Student::getName)
                                     .collect(joining(",","{","}"));
        System.out.println(stuNames); 
        /* {이자바,김자바,안자바,박자바,소자바,나자바,감자바} */
        
        /* map() 없이 스트림에 바로 joining을 한 것은 스트림요소에 toString을 호출한 결과를 결합한다. */
        String studentInfo = Stream.of(stuArr).collect(joining(","));
    }
}
     Class Student implements Comparable<Student>{
        String name;
        int ban;
        int totalScore;

        Student(String name ,int ban, int totalScore){
            this.name = name;
            this.ban = ban;
            this.totalScore = totalScore;
        }
        public String toString(){
            return String.format("[%s, %d, %d]", name, ban, totalScore).toString();
        }
}

 

분할 - partitioningBy()에 의한 분류
: 스트림의 요소를 두 가지, 지정된 조건에 일치하는 그룹과 일치하지 않는 그룹으로 분할할 수 있다.
partitioningBy는 Predicate로 분류한다.
반환형은 Map이다. 

1️⃣ 기본 분할

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* Student클래스의 변수 isMale은 성별을 나타낸다. 남자는 true, 여자는 false */
Map<Boolean, List<Student>> stuBySex = stuStream
                                     .collect(partitioningBy(Student::isMale));
                                     
 List<Student> maleStudent = stuBySex.get(true);  // map에서 남학생 목록을 얻는다.        
 List<Student> femaleStudent = stuBySex.get(false); // map에서 여학생 목록을 얻는다.

2️⃣ 기본분할 + 통계정보 

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* Student클래스의 변수 isMale은 성별을 나타낸다. 남자는 true, 여자는 false */
/* 성별 수 */
Map<Boolean, Long> stuNumBySex = stuStream
                                  .collect(partitioningBy(Student::isMale,counting()));
                                     
 Long mCnt  = stuNumBySex.get(true);  // 남학생의 수     
 Long wCnt = stuNumBySex.get(false); // 여학생의 수
 
 /* 성별 1등 */
 Map<Boolean, Optional<Student>> topScoreBySex = stuStream
                                  .collect(
                                  partitioningBy(Student::isMale
                                        ,maxBy(comparingInt(Student::getScore)))
                                  );
                                     
 Optional<Student> mTop  = topScoreBySex.get(true);  // 남학생 1등    
 Optional<Student> wTop  = topScoreBySex.get(false); // 여학생 1등
 
 /* -> 반환형을 클래스(student)로 얻으려면 */
  Map<Boolean, Student> topScoreBySex = stuStream
                                  .collect(
                                  partitioningBy(Student::isMale
                                   ,collectingAndThen(
                                        maxBy(comparingInt(Student::getScore)),
                                        Optional::get
                                      )
                                    )
                                  );
                                     
Student mTop  = topScoreBySex.get(true);  // 남학생 1등    
Student wTop  = topScoreBySex.get(false); // 여학생 1등

3️⃣ 이중 분할

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* Student클래스의 변수 isMale은 성별을 나타낸다. 남자는 true, 여자는 false */
/* 성별로 총점이 150 미만인 학생들 */
Map<Boolean, List<Student>> failedStuBySex 
                      = stuStream.collect(
                               partitioningBy(Student::isMale,
                                   partitioningBy(s -> s.getScore() < 150)
                                 )
                              );
                                     
 List<Student> maleStudent = failedStuBySex.get(true).get(true);         
 List<Student> femaleStudent = failedStuBySex.get(false).get(true);

 

그룹화 - groupingBy()에 의한 분류
: 스트림의 요소를 특정 기분으로 그룹화시킨다.
groupingBy는 Function으로 분류한다.
반환형은 Map이다. 

1️⃣ 기본 그룹화

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* 반별로 학생을 그룹화하기 */
Map<Boolean, List<Student>> stuByBan = stuStream
                                     .collect(groupingBy(Student::getBan));
                                     
/*groupingBy()로 그룹화를 하면 기본적으로 List<T>에 담는다
. 그래서 기본적으로 toList()는 생략이 가능하다 .*/
/* 변환하고 싶은 컬렉션을 지정할 수 있다. 메소드가 정의되지않았다면 커스텀이 가능하다. */

Map<Boolean, List<Student>> stuByBan = stuStream
                                     .collect(groupingBy(Student::getBan,toList()));
                                     
Map<Boolean, HashSet<Student>> stuByHak = stuStream
                    .collect(groupingBy(Student::getHak,toCollection(HashSet::new)));

2️⃣ 심화 그룹화 

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* 성적의 등급(high, mid, row)으로 그룹화하기  */
Map<Student.Level, Long> stuByLevel = stuStream
                          .collect(groupingBy(s-> {
                          if(s.getScore() >= 200) return Student.Level.HIGH;
                          else if(s.getScore() >= 100) return Student.Level.MID;
                          else                      return Student.Level.LOW;
                          },counting())
                        );

3️⃣ 다수준 그룹화

/* stuStream가 요소가 클래스 Student로 이루어져 있다고 가정한다. */
/* 학년별로 그룹화한뒤 다시 반별로 그룹화하기   */
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream
                          .collect(groupingBy(Student::getHak,
                          groupingBy(Student::getBan)
                       ));
                       
                       
/* 각 반의 1등 추출하기 */    
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream
                          .collect(groupingBy(Student::getHak,
                          groupingBy(Student::getBan,
                          collectionAndThen(
                              maxBy(ComparingInt(Student::getScore)),
                              Optional::get)
                          )
                       ));
728x90
반응형

'개발 공부 > Java & Spring' 카테고리의 다른 글

25. 문자기반의 보조스트림  (0) 2023.04.09
24. Collector 구현하기  (0) 2023.03.29
22. 스트림의 최종연산  (0) 2023.03.26
21. fork & join 프레임워크  (0) 2023.03.09
20. volatile  (0) 2023.03.09