본문 바로가기

BackEnd/Java

[java] 이것이 자바다 ch17 스트림(reduce, 병렬)

728x90
반응형

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

 

스트림을 사용해보니 다양한 메소드들이 있어서 한 단원의 비중을 차지하네요!

 

계속 유사한 것들을 사용해봐야겠어요!!

 

많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~

 

728x90
반응형