모르지 않다는 것은 아는것과 다르다.

Java

Collectors (JAVA STREAM)

채마스 2022. 3. 6. 23:17

Collector<T, ?, List> toList();

  • Stream 안의 데이터를 List 형태로 반환해주는 collector.
List<Integer> numberList = Stream.of(3, 5, -3, 3, 4, 5)
        .collect(Collectors.toList());

 

Collector<T, ?, Set> toSet();

  • Stream 안의 데이터를 Set 형태로 반환해주는 collector.
  • Set이기 때문에 중복값은 사라지고 순서가 무의미해짐에 유의해야 한다.
Set<Integer> numberSet = Stream.of(3, 5, -3, 3, 4, 5)
        .collect(Collectors.toSet());

 

<T, U, A, R> Collector<T, ?, R> mapping( Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream);

  • Map과 collect를 합쳐놓은 역할을 해주는 collector. 일반적으로는 map을 한 후 collect를 해도 되지만 groupingBy 등 필요할 때가 있다.
List<Integer> numberList2 = Stream.of(3, 5, -3, 3, 4, 5)
                .collect(Collectors.mapping(x -> Math.abs(x), Collectors.toList()));

 

Collector<T, ?, T> reducing( T identity, BinaryOperator op);

  • reduce를 해주는 collector.
int sum = Stream.of(3, 5, -3, 3, 4, 5)
                .collect(Collectors.reducing(0, (x, y) -> x + y));

 

<T, K, U> Collector<T, ?, Map<K,U>> toMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)

  • Stream 안의 데이터를 map의 형태로 반환해주는 collector
  • keyMapper – 데이터를 map의 key로 변환하는 Function
  • valueMapper – 데이터를 map의 value로 변환하는 Function
Map<Integer, String> numberMap = Stream.of(3, 5, -4, 2, 6)
        .collect(Collectors.toMap(x -> x, x -> "Number is " + x));
  • key 는 x -> x 가 되고, value 는 "Number is " + x 가 된다.
  • x -> x 는 Function.identity() 로 대체할 수 있다.
User user1 = new User()
        .setId(101)
        .setName("Alice")
        .setVerified(true);
User user2 = new User()
        .setId(102)
        .setName("Bob")
        .setVerified(false);
User user3 = new User()
        .setId(103)
        .setName("Charlie")
        .setVerified(false);
List<User> users = Arrays.asList(user1, user2, user3);

Map<Integer, User> userIdToUserMap = users.stream()
        .collect(Collectors.toMap(User::getId, Function.identity()));
  • 위와 같이 User 객체를 담은 리스트에서 User 의 id 를 key 로 하고 id 에 해당하는 User 를 value 로 하는 map 을 만들 수 있다.

 

<T, K> Collector<T, ?, Map<K, List>> groupingBy(Function<? super T, ? extends K> classifier)

  • Stream 안의 데이터에 classifier를 적용했을 때 결과값이 같은 값끼리 List로 모아서 Map의 형태로 반환해주는 collector이다.
  • 예를 들어, stream 에 {1, 2, 5, 7, 9, 12, 13}이 있을 때 classifier 가
    x -> x % 3 이라면 반환되는 map은 {0 = [9, 12], 1 = [1, 7, 13], 2 = [2, 5]} 가 된다.
List<Integer> numbers = Arrays.asList(13, 2, 101, 203, 304, 402, 305, 349, 2312, 203);
        Map<Integer, List<Integer>> unitDigitMap = numbers.stream()
                .collect(Collectors.groupingBy(number -> number % 10));

 

<T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy( Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream);

  • 두 번째 매개변수로 downstream collector를 넘길 수 있다.
  • 그 경우 List 대신 collector를 적용시킨 값으로 map의 value가 만들어진다.
  • 이 때 자주 쓰이는 것이 mapping / reducing 등의 collector 이다.
List<Integer> numbers = Arrays.asList(13, 2, 101, 203, 304, 402, 305, 349, 2312, 203);
Map<Integer, Set<Integer>> unitDigitSet = numbers.stream()
                .collect(Collectors.groupingBy(number -> number % 10, Collectors.toSet()));
List<Integer> numbers = Arrays.asList(13, 2, 101, 203, 304, 402, 305, 349, 2312, 203);
Map<Integer, List<String>> unitDigitStrMap = numbers.stream()
                .collect(Collectors.groupingBy(number -> number % 10,
                        Collectors.mapping(number -> "unit digit is " + number, Collectors.toList())));
  • 위와 같이 mapping 을 이용해서 숫자를 문자로 바꿔서 반환할 수 있다.
  • unitDigitStrMap 가 Map 이기 때문에 unitDigitStrMap.get(key) 로 결과를 확인할 수 있다.
Order order1 = new Order()
        .setId(1001L)
        .setAmount(BigDecimal.valueOf(2000))
        .setStatus(OrderStatus.CREATED);
Order order2 = new Order()
        .setId(1002L)
        .setAmount(BigDecimal.valueOf(4000))
        .setStatus(OrderStatus.ERROR);
Order order3 = new Order()
        .setId(1003L)
        .setAmount(BigDecimal.valueOf(3000))
        .setStatus(OrderStatus.ERROR);
Order order4 = new Order()
        .setId(1004L)
        .setAmount(BigDecimal.valueOf(7000))
        .setStatus(OrderStatus.PROCESSED);
List<Order> orders = Arrays.asList(order1, order2, order3, order4);

Map<OrderStatus, BigDecimal> orderStatusToSumOfAmountMap = orders.stream()
        .collect(Collectors.groupingBy(Order::getStatus, // Map<OrderStatus, List<Order>>
                Collectors.mapping(Order::getAmount,  // Map<OrderStatus, List<BigDecimal>>
                        Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)))); //Map<OrderStatus, BigDecimal>
  • 위의 코드는 주문 목록에서 주문 상태별 총 합개를 구하는 과정이다.
  • 먼저 groupingBy 로 주문상태 : 주문록록 의 Map 으로 묶는다.
  • 그 다음 mapping 을 이용해서 주문목록에서 금액만 뽑아온다.
  • 마지막으로 reducing 을 이용해서 주문 목록별로 합계를 구한다.
  • 결과로는 {ERROR = 7000, PROCESSED=7000, CREATE=2000} 가 된다.

 

Collector<T, ?, Map<Boolean, List>> partitioningBy(Predicate<? super T> predicate)

  • GroupingBy와 유사하지만 Function 대신 Predicate을 받아 true와 false 두 key가 존재하는 map을 반환하는 collector 이다.
List<Integer> numbers = Arrays.asList(13, 2, 101, 203, 304, 402, 305, 349, 2312, 203);
Map<Boolean, List<Integer>> numberPartitions = numbers.stream()
        .collect(Collectors.partitioningBy(number -> number % 2 == 0));

for(Integer evenNumber : numberPartitions.get(true)) {
    System.out.println(evenNumer + ",")
}

for(Integer oddNumber : numberPartitions.get(false)) {
    System.out.println(oddNumber + ",")
}
  • 위와 같이 true 인값과 false 인 값으로 Map 이 형성되고, 반복문으로 각각의 값을 확인할 수 있다.

 

<T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream)

  • downstream collector를 넘겨 List 이외의 형태로 map의 value를 만드는 것 역시 가능하다.

Stream 은 Lazy Evaluation 이 적용되어 있는 것이다.

Collecors 가 나와서야 값을 계산한다.




REFERENCES

  • 이승환님의 JAVA Stream

'Java' 카테고리의 다른 글

ENUM 활용  (0) 2022.03.14
Method Reference  (0) 2022.03.06
LazyEvaluation,Curry,IntStream  (0) 2022.03.06
Stream 응용  (0) 2022.03.06
함수형 인터페이스  (0) 2022.02.26