11. 요소 커스텀 집계(스트림이 제공하는 메소드)
▷ 스트림은 기본 집계 메소드인 sum(), average(), count(), max(), min()을 제공하지만,
다양한 집계 결과물을 만들 수 있도록 reduce() 메소드도 제공
NO | 인터페이스 | 리턴 타입 | 메소드(매개변수) |
1 | Stream | Optional<T> | reduce(BinaryOperator<T> accumulator) |
2 | T | reduce(T identity, BinaryOperator<T> accumulator) | |
3 | IntStream | OptionalInt | reduce(IntBinaryOperator op) |
4 | int | reduce(int identity, IntBinaryOperator op) | |
5 | LongStream | OptionalLong | reduce(LongBinaryOperator op) |
l6 | long | reduce(long identity, LongBinaryOperator op) | |
7 | DoubleStream | OptionalDouble | reduce(DoubleBinaryOperator op) |
8 | double | reduce(double identity, DoubleBinaryOperator op) |
▷ reduce()는 스트림에 요소가 없을 경우 예외가 발생하지만, identity 매개값이 주어지면 이 값을 디폴트 값으로 리턴
1. Student 클래스
public class Student { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } }
2. ReductionExample 메인 클래스
import java.util.Arrays; import java.util.List; public class ReductionExample { public static void main(String[] args) { List<Student> studentList = Arrays.asList( new Student("홍길동", 92), new Student("신용권", 95), new Student("감자바", 88) ); // 방법1 int sum1 = studentList.stream() .mapToInt(Student :: getScore) .sum(); // 방법2 int sum2 = studentList.stream() .map(Student :: getScore) .reduce(0, (a,b) -> (a+b)); System.out.println("sum1: " + sum1); System.out.println("sum2: " + sum2); } } // 출력 : // sum1: 275 // sum2: 275
12. 필터링한 요소 수집
▷ Stream의 collect(Collector<T,A,R> collectior) 메소드는 필터링 또는 매핑된 요소들을 새로운 컬렉션에 수집하고, 이 컬렉션을 리턴
▷ 매개값인 Collector는 어떤 요소를 어떤 컬렉션에 수집할 것인지를 결정
▷ 타입 파라미터의 T는 요소, A는 누적기 accumulator, 그리고 R은 요소가 저장될 컬렉션
NO | 리턴 타입 | 메소드(매개변수) | 인터페이스 |
1 | R | collect(Collector<T,A,R> collector) | Stream |
NO | 리턴 타입 | 메소드 | 설명 |
1 | Collector<T, ?, List<T>> | toList() | T를 List에 저장 |
2 | Collector<T, ?, Set<T>> | toSet() | T를 Set에 저장 |
3 | Collector<T, ?, Map<K, U>> | toMap( Function<T,K> keyMapper, Function<T,U> valueMapper ) |
T를 K와 U로 매핑하여 K를 키로, U를 값으로 Map에 저장 |
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CollectionExample { public static void main(String[] args) { List<Student> totalList = new ArrayList<>(); totalList.add(new Student("홍길동", "남", 92)); totalList.add(new Student("김수영", "여", 87)); totalList.add(new Student("감자바", "남", 95)); totalList.add(new Student("오해영", "여", 93)); // 남학생만 묶어 List 생성 List<Student> maleList = totalList.stream() .filter(s -> s.getSex().equals("남")) .collect(Collectors.toList()); // java8에서는 사용 안됨 // List<Student> maleList = totalList.stream() // .filter(s -> s.getSex().equals("남")) // .toList(); maleList.stream() .forEach(s -> System.out.println(s.getName())); System.out.println(); // 학생 이름을 키, Student 객체를 값으로 갖는 Map 생성 Map<String, Integer> map = totalList.stream() .collect( Collectors.toMap( s -> s.getName(), s -> s.getScore() ) ); System.out.println(map); } } // 출력 : // 홍길동 // 감자바 // // {오해영=93, 홍길동=92, 감자바=95, 김수영=87}
▷ toList() 메소드는 java8에서는 적용불가능합니다.
◎ 요소 그룹핑
▷ Collectors.groupingBy() 메소드에서 얻은 Collector를 collect() 메소드를 호출할 때 제공
▷ groupingBy()는 Function을 이용해서 T를 K로 매핑하고, K를 키로 해 List<T>를 값으로 갖는 Map 컬렉션을 생성
NO | 리턴 타입 | 메소드 |
1 | Collector<T,?,Map<K,List<T>>> | groupingBy(Function<T,K> classifier) |
위의 Student 클래스를 동일하게 사용하고 CollectExample2 메인 클래스를 만들어줍니다.
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CollectExample2 { public static void main(String[] args) { List<Student> totalList = new ArrayList<>(); totalList.add(new Student("홍길동", "남", 92)); totalList.add(new Student("김수영", "여", 87)); totalList.add(new Student("감자바", "남", 95)); totalList.add(new Student("오해영", "여", 93)); // groupingBy로 정한 것이 map의 key가 되며, 값은 List<Student> 형태입니다. Map<String, List<Student>> map = totalList.stream() .collect( Collectors.groupingBy(s -> s.getSex()) ); List<Student> maleList = map.get("남"); maleList.stream().forEach(s -> System.out.println(s.getName())); System.out.println(); List<Student> femaleList = map.get("여"); femaleList.stream().forEach(s -> System.out.println(s.getName())); } } // 출력 : // 홍길동 // 감자바 // // 김수영 // 오해영
▷ Collectors.groupingBy() 메소드는 그룹핑 후 매핑 및 집계(평균, 카운팅, 연결, 최대, 최소, 합계)를 수행할 수 있도록 두 번째 매개값인 Collector를 가질 수 있습니다.
NO | 리턴 타입 | 메소드(매개변수) | 설명 |
1 | Collector | mapping(Function, Collector) | 매핑 |
2 | Collector | averagingDouble(ToDoubleFunction) | 평균값 |
3 | Collector | counting() | 요소 수 |
4 | Collector | maxBy(Comparator) | 최대값 |
5 | Collector | minBy(Comparator) | 최소값 |
6 | Collector | reducing(BinaryOperator<T>) reducing(T identity, BinaryOperator<T>) |
커스텀 집계 값 |
위의 Student 클래스를 동일하게 사용하고 CollectExample3 메인 클래스를 만들어줍니다.
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CollectExample3 { public static void main(String[] args) { List<Student> totalList = new ArrayList<>(); totalList.add(new Student("홍길동", "남", 92)); totalList.add(new Student("김수영", "여", 87)); totalList.add(new Student("감자바", "남", 95)); totalList.add(new Student("오해영", "여", 93)); Map<String, Double> map = totalList.stream() .collect( Collectors.groupingBy(s -> s.getSex(), Collectors.averagingDouble(s -> s.getScore()) ) ); System.out.println(map); } } // 출력 : // {남=93.5, 여=90.0}
13. 요소 병렬 처리
◎ 동시성과 병렬성
▷ 동시성 : 멀티 작업을 위해 멀티 스레드가 하나의 코어에서 번갈아가며 실행하는 것
▷ 병렬성 : 멀티 작업을 위해 멀티 코어를 각각 이용해서 병렬로 실행하는 것
▷ 데이터 병렬성 : 전체 데이터를 분할해서 서브 데이터셋으로 만들고 이 서브 데이터셋들을 병렬처리해서 작업을 빨리 끝내는 것
▷ 작업 병렬성 : 서로 다른 작업을 병렬 처리하는 것
◎ 포크조인 프레임워크
▷ 포크 단계 : 전체 요소들을 서브 요소셋으로 분할하고, 각각의 서브 요소셋을 멀티 코어에서 병렬로 처리
▷ 조인 단계 : 서브 결과를 결합해서 최종 결과를 만들어냅니다.
▷ 포크조인 프레임워크는 ExecutorService의 구현 객체인 ForkJoinPool을 사용해서 작업 스레드를 관리
◎ 병렬 스트림 사용
▷ 자바 병렬 스트림은 백그라운드에서 포크조인 프레임워크가 사용하므로 병렬 처리 용이
▷ parallelStream() 메소드는 컬렉션(List, Set)으로부터 병렬 스트림을 바로 리턴
▷ parallel() 메소드는 기존 스트림을 병렬 처리 스트림으로 변환
NO | 리턴 타입 | 메소드 | 제공 컬렉션 또는 스트림 |
1 | Stream | parallelStream() | List 또는 Set 컬렉션 |
2 | Stream | parallel() | java.util.Stream |
3 | IntStream | java.util.IntStream | |
4 | LongStream | java.util.LongStream | |
5 | DoubleStream | java.util.DoubleStream |
◎ 병렬 처리 성능에 영향을 미치는 요인
▷ 요소의 수와 요소당 처리 시간
▷ 스트림 소스의 종류
▷ 코어(Core)의 수
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.stream.Stream; public class ParallelExample { public static void main(String[] args) { Random random = new Random(); List<Integer> scores = new ArrayList<>(); for(int i=0; i<100000000; i++) { scores.add(random.nextInt(101)); } double avg = 0.0; long startTime = 0; long endTime = 0; long time = 0; Stream<Integer> stream = scores.stream(); startTime = System.nanoTime(); avg = stream .mapToInt(i -> i.intValue()) .average() .getAsDouble(); endTime = System.nanoTime(); time = endTime - startTime; System.out.println("avg: " + avg + ", 일반 스트림 처리 시간: " + time + "ns"); Stream<Integer> parallelStream = scores.parallelStream(); startTime = System.nanoTime(); avg = parallelStream .mapToInt(i -> i.intValue()) .average() .getAsDouble(); endTime = System.nanoTime(); time = endTime - startTime; System.out.println("avg: " + avg + ", 병렬 스트림 처리 시간: " + time + "ns"); } } // 출력 : // avg: 49.99817058, 일반 스트림 처리 시간: 205250700ns // avg: 49.99817058, 병렬 스트림 처리 시간: 80448000ns
스트림을 사용해보니 다양한 메소드들이 있어서 한 단원의 비중을 차지하네요!
계속 유사한 것들을 사용해봐야겠어요!!
많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~
'BackEnd > Java' 카테고리의 다른 글
[java] 이것이 자바다 ch18 데이터 입출력 2 (Writer, Reader) (0) | 2023.01.31 |
---|---|
[java] 이것이 자바다 ch18 데이터 입출력 1 (Writer, Reader) (0) | 2023.01.30 |
[java] 이것이 자바다 ch17 스트림(sorted, 루핑, 매칭, optional) (0) | 2023.01.30 |
[백준 문제 10250번] ACM 호텔 문제 (0) | 2023.01.29 |
[java] 이것이 자바다 ch17 스트림(필터링, 매핑, wrapper) (0) | 2023.01.28 |