스트림으로 데이터 수집

HeoSeungYeon·2021년 8월 23일
0

Java Study

목록 보기
7/9
post-thumbnail

개요


이전 포스팅에서 Stream API의 최종 연산(Terminal Method)에 쓰이는 Collect 를 간단하게 다뤄봤습니다. Collect는 Collector 타입의 매개변수를 전달받아 원하는 자료구조로 변환시켜주는 기능이 대표적인데요. 이 기능 이외에도 Join 연산, 수학 연산 등의 기능을 제공합니다. 이번 시간에는 Collect를 잘 활용할 수 있도록 기능들에 대해서 배워보고 실습해보는 시간을 가져보겠습니다.☺️

0. Stream.collect() Method


Stream.collect() Method는 Java8 의 Stream API의 최종 연산(terminal method) 중 하나 입니다. collect() 메서드는 스트림의 데이터 요소들을 특정 자료구조로 변환하거나, 특정 논리를 적용시킨 결과를 반환해주는데요, 이 collect() 메서드의 핵심은 Collector 인터페이스 구현을 통해 제공되어집니다.

1. Collectors


import java.util.stream.Collectors;

Collectors 인터페이스는 CollectorImpl 내부 클래스 라는 구현체를 가지고 있습니다. Stream API 의 collect() 의 toList(), toSet() 메서드는 ColletorImpl 객체를 생성하여 반환하는 형식으로 구현되어져있는데요.

먼저 자료구조 변환 기능에 해당하는 연산부터 알아보는 시간을 가져보겠습니다.

2. Collect Function


2-1) 자료구조 변환 연산


(1) Collectors.toList()


Collectors.toList() 메서드는 스트림의 요소들을 모아 List 인스턴스를 반환해주는데요.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

List<String> sportList = Arrays.stream(sports).collect(Collectors.toList());

System.out.println(sportList.toString());
 
// Result
// [soccer, football, baseball, basketball]

추가로, Java 10 버전에서 생긴 기능인 toUnmodifiableList() 메서드는 반환된 List 객체를 수정불가능하게끔 만들어줍니다. 만약 수정을 할 시, UnsupportedOperationException 예외를 발생시키는 것을 확인할 수 있습니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

//List<String> sportList = Arrays.stream(sports).collect(Collectors.toList());
List<String> sportList = Arrays.stream(sports).collect(Collectors.toUnmodifiableList());

sportList.add("tennis");

System.out.println(sportList.toString());

결과

(2) Collectors.toSet()


Collectors.toSet() 메서드는 스트림의 요소들을 모아 Set 인스턴스를 반환해주는데요.

예제 코드

String[] sports = {"soccer","football","football","basketball"};

Set<String> sportSet = Arrays.stream(sports).collect(Collectors.toSet());

System.out.println(sportSet.toString());

결과

[soccer, basketball, football]

Set 자료형은 요소의 중복을 제거하기 때문에, 중복이 제거된 형태의 출력값을 확인할 수 있었습니다.

마찬가지로, Java 10 버전에서 수정불가능한 Set 자료형을 만들어주는 toUnmodifiableSet() 메서드의 기능을 제공하였는데요. 만약 수정을 할 시, UnsupportedOperationException 예외를 발생시키는 것을 확인할 수 있습니다.

예제 코드

String[] sports = {"soccer","football","football","basketball"};

Set<String> sportSet = Arrays.stream(sports).collect(Collectors.toUnmodifiableSet());

sportSet.add("basketball");

System.out.println(sportSet.toString());

결과

(3) Collectors.toCollection()


위에서 살펴본 toList(), toSet()은 특정 Collection(ArrayList, LinkedList, HashSet 등)으로 변환이 제한되어져 있었는데요. 그래서 만약 특정 Collection으로 만들어주어야 할때는 Collectors.toCollection() 메서드를 사용하면 가능합니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

List<String> sportList = Arrays.stream(sports).collect(Collectors.toCollection(ArrayList::new));

System.out.println(sportList.toString());
System.out.println(sportList.getClass());

결과

[soccer, football, baseball, basketball]
class java.util.ArrayList

(4) Collectors.toMap()


Collectors.toMap() 메서드는 스트림의 요소들을 모아 Map 인스턴스를 반환해주는데요. toMap() 메서드는 keyMapper, valueMaaper 를 매개변수로 사용합니다. → toMap(keyMaaper, valueMapper)
예제로는 sports의 요소들이 key 값을 갖고, 요소의 길이를 value로 갖게 하는 Map 객체를 만들게끔 구현하였습니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

Map<String,Integer> sportMap = Arrays.stream(sports).collect(Collectors.toMap(String::toString, String::length));

System.out.println(sportMap.toString());
System.out.println(sportMap.getClass());

결과

{soccer=6, basketball=10, baseball=8, football=8}
class java.util.HashMap

만약 키가 중복 되면 어떻게 될까요?

예제 코드

String[] sports = {"soccer","football","football","basketball"};

Map<String,Integer> sportMap = Arrays.stream(sports).collect(Collectors.toMap(String::toString, String::length));

System.out.println(sportMap.toString());
System.out.println(sportMap.getClass());

결과

"football"이라는 key가 중복되어 IllegalStateException 예외를 발생시키는 것을 확인할 수 있습니다. 이럴 땐 다음과 같이 중복에 대한 처리를 할 수 있습니다.

예제 코드

String[] sports = {"soccer","football","football","basketball"};

Map<String,Integer> sportMap = Arrays.stream(sports).collect(Collectors.toMap(String::toString, String::length,(post, pre) -> post));

System.out.println(sportMap.toString());
System.out.println(sportMap.getClass());

결과

{soccer=6, basketball=10, football=8}
class java.util.HashMap

toMap() 메서드의 세번째 인자(mergeFunction)에 (post, pre) -> post) 함수를 전달시켜줌으로써 동일한 키가 들어올 경우 기존의 키를 선택하는 방식으로 중복 처리를 진행하였습니다.

Collectors - toMap 메서드

(5) Collectors.collectingAndThen()


Collectors.collectingAndThen() 메서드는 Collecting 을 진행한 결과를 바탕으로 메서드를 추가로 진행할 수 있게 해주는 메서드입니다.

예제에선 만들어진 리스트의 toString() 메서드를 호출하게 구현하였습니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

System.out.println((String) Arrays.stream(sports).collect(Collectors.collectingAndThen(Collectors.toList(),Collection::toString)));

결과

[soccer, football, baseball, basketball]

2-2) 이외 기능(join, groupingBy, partition)


collect() 메서드는 자료구조로 변환시켜주는 기능 이외에 Stream의 요소들을 특정 조건으로 join, grouping, partitioning 을 수행할 수 있는 기능을 제공하는데요. 이 3가지 기능을 중점적으로 공부하고 실습해보는 시간을 가져보도록 하겠습니다.

(1) Collectors.joining()


joining 메서드는 List 가 아닌 String으로 반환시켜주는 기능을 수행합니다.

delimiter(구분자) 를 매개변수로 전달하여 join 연산을 수행할 수 있습니다.

예제코드

String[] sports = {"soccer","football","baseball","basketball"};

String sportStr = Arrays.stream(sports).collect(Collectors.joining(" + "));

System.out.println(sportStr);

결과

soccer + football + baseball + basketball

(2) Collectors.groupingBy()


Collectors.groupingBy()은 데이터베이스의 테이블 조작 연산 중 하나인 groupby 연산을 제공해줍니다.

groupingBy의 매개변수는 classifier, downStream 으로 이루어져 있는데

classifier에 그룹핑할 기준을, downStream을 그룹핑 결과를 어떤 자료구조로 반환시킬까에 대한 결정을 할 메소드를 전달할 수 있습니다.

예제에선 스포츠의 문자열 길이를 기준으로 그룹핑한 결과를 List로 반환하게끔 구현하였습니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

Map<Integer, List<String>> sportGroupMap = Arrays.stream(sports).collect(Collectors.groupingBy(String::length,Collectors.toList()));

System.out.println(sportGroupMap);

결과

{6=[soccer], 8=[football, baseball], 10=[basketball]}

(3) Collectors.partitioningBy()


Collectors.partitioningBy()은 Collectors.groupingBy()를 응용하여 Predicate 함수형 인터페이스의 반환값이 true인 집합군과 false인 집합군을 Map 형태로 반환해줍니다.

예제에선, 스포츠의 문자열 길이가 6이하인 경우 true를 반환하는 Predicate 함수형 인터페이스 구현체를 전달해보았습니다.

예제 코드

String[] sports = {"soccer","football","baseball","basketball"};

Map<Boolean, List<String>> sportPartitionMap = Arrays.stream(sports).collect(Collectors.partitioningBy(s->s.length()<=6));

System.out.println(sportPartitionMap);

결과

{false=[football, baseball, basketball], true=[soccer]}

참고자료


Java8의 Stream Collect 사용 방법 및 예제

(6) 스트림으로 데이터 수집

[Java 8] Java 8, Stream의 Collectors

0개의 댓글