자바 Stream Collect & Collector

Martin the dog·2023년 10월 20일

Collect & Collector & Collectors

Stream의 중간 연산을 통해 나온 결과를 우리가 원하는 Collection 자료형의 형태로 바꿔야 한다.
우리가 원하는 자료형으로 바꿔주는 Stream의 최종 연산이 collect이다.

Collect

Stream.collect()는 앞서 설명한 것 처럼 Stream의 결과를 원하는 자료형으로 바꿔 반환해주는 최종 연산자이다. Stream.collect()는 Stream을 특정 자료형으로 바꿔주는 Collector 인터페이스 또는 (Supplier, BiConsumer for accumulator, BiConsumer for combiner) 3개의 함수를 받는다.

Supplier는 ArrayList::new처럼 값을 누적하여 받을 Collection을 생성하는 함수이다.
BiConsumer for accumulator는 Supplier 함수에서 나온 Collection에 값을 어떻게 넣을지 정해주는 함수이다. 여기까지는 이전에 배운 reduce와 같다.
BiConsumer for combiner는 병렬연산시 각 쓰레드 마다 나온 stream의 결과값을 어떻게 병합해줄 것인지를 정해주는 함수이다. 병렬연산시에만 사용하면 된다.

자 이제 예시 시간! 먼저 다음과 같은 ArrayList가 있다고 하자

List<menu> menus = new ArrayList<menu>();
        menus.add(new menu("스테이크",500,food.BEEF));
        menus.add(new menu("불고기",400,food.BEEF));
        menus.add(new menu("불고기 스테이크",500,food.BEEF));
        menus.add(new menu("삼겹살",300,food.PORK));
        menus.add(new menu("대패삼겹살",400,food.PORK));
        menus.add(new menu("대승삼겹살",200,food.PORK));
        menus.add(new menu("이베리코 삼겹살",500,food.PORK));

이제 이걸 지금은 Enum값인 food에 따라 구분하여 hashMap에 저장하고 싶다. 거기에 가격이 300이상만 해야한다면 어떻게 해야할까?
답은 다음과 같다.

HashMap<String,List<menu>> mapMenuResult = menus.stream().filter((e)->{
            return e.calorie>=300;
        }).collect(HashMap<String,List<menu>>::new,(mapMenu,menuItem)->{
            if(mapMenu.get(menuItem.foodType.getDesc())!=null){
                mapMenu.get(menuItem.foodType.getDesc()).add(menuItem);
            }
            else{
                List<menu> tmpList = new ArrayList<>();
                tmpList.add(menuItem);
                mapMenu.put(menuItem.foodType.getDesc(), tmpList);
            }
        }
        ,(map1,map2)->{//다른 쓰레드에서 실행된 stream의 결과를 병합한다.
            map1.putAll(map2);
        });

완젼 간결 그자체!
이렇게 내가 직접 구현할 수도 있지만 이미 이런 작업을 해주는 Collector의 구현체인 Collectors에 정의된 메쏘드들이 많이 있다.

Collectors

다음은 내가 생각하기에 중요하다 생각되는 Collectors의 메쏘드들을 소개한다.

Collectors.toList()

List 형태로 바꿔준다. 특별한 것들은 없당.

Collectors.joining("분리자")

문자열로 이루어진 Stream을 주어진 분리자로 연결시켜준다.

Collectors.groupingBy((T)->U)

그룹지어줄 기준을 반환해주는 함수를 받아 해당 기준값을 Key로한 HashMap<String,List<~>>를 반환한다.
위 코드에 적용하면 다음과 같이 변한다.

  Map<food, List<menu>> mapMenuResult = menus.stream().filter((e)->{
            return e.calorie>=300;
        }).collect(groupingBy(menu::getType));

요로코롬 간결하게 바뀐다.

그 외에도 다양한 Collectors가 많지만 다 외우는 것 보다는
>Java Collectors<
에서 직접 보는게 좋다

profile
Happy Developer

0개의 댓글