JAVA/Java 기초

요소 커스텀 집계.java

john_ 2023. 1. 30. 20:18
728x90

2023.01.30 - [JAVA/Java 기초] - Optional 클래스.java

 

Optional 클래스.java

Optional 클래스 java.util.Optional Optional은 선택형 값을 캡슐화 하는 클래스 입니다. 값이 있으면 Optional 클래스는 값을 감쌉니다. 값이 없으면 Optional.empty 메서드로 Optional을 반환 합니다. 값이 없을경

less-go.tistory.com

 

 


스트림이 제공하는 메소드

  • 스트림은 기본 집계 메소드인 sum(), average(), count(), max(), min()을 제공하지만, 다양한 집계 결과물을 만들수 있도록 redeuce() 메소드도 제공합니다.

  • 매개값인 BinaryOperator는 함수형 인터페이스 입니다.
  • BinaryOperator는 두개의 매개값을 받아 하나의 값을 리턴하는 apply() 메소드를 가지고 있어, 다음과 같이 람다식을 작성할수 있습니다.
(a, b) -> { ... return 값;}
또는
(a, b) -> 값	
//return 문만 있을 경우 중괄호와 return 키워드를 생략 가능합니다.

 

  • reduce()는 스트림에 요소가 없을 경우 예외가 발생하지만, identify 매개값이 주어지면 이 값을 디폴트 값으로 리턴합니다.
int sum = stream
	.reduce((a, b) -> a+b)
    .getAsInt();
    // 스트림에 요소가 없을경우 NoSuchElementException을 발생 시킵니다.
int sum = stream
	.reduce(0, (a,b) -> a+b);
// 디폴트 값 (identity)인 0을 리턴합니다.

 


Student.java

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;}
}
ReductionExam.java

import java.util.Arrays;
import java.util.List;

public class ReductionExam {

	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

 


필터링 한 요소 수집

  • Stream의 collect(Collector<T,A,R> collector) 메소드는 필터링 또는 매핑된 요소들을 새로운 컬렉션에 수집하고, 이 컬렉션을 리턴합니다.
  • 매개값인 Collector는 어떤 요소를 어떤 컬렉션에 수집할 것인지를 결정합니다.
  • 타입 파라미터의 T는 요소, A는 누적기 accumulator, 그리고 R은 요소가 저장될 컬렉션

 


Student 스트림에서 남학생만 필터링해서 별도의 List로 생성하는 코드입니다.

List<Student> maleList = totalList.stream()
	.filter(s->s.getSex().equals("남"))	//남학생만 필터링
    	.collect(Collecotrs.toList());

Student 스트림에서 이름을 키로, 점수를 값으로 갖는 Map 컬렉션을 생성하는 코드입니다.

Map<String, Integer> map = totoalList.stream()
	.collect(
		Collectors.toMap(
        	s->s.getName(),	//Student 객체에서 키가 될 부분 리턴
            s->s.getScore()	//Student 객체에서 값이 될 부분 리턴
        )
    );

Java 16부터 편리하게 요소 스트림에서 List 컬렉션을 얻는 방법 = 스트림에서 바로 toList() 메소드를 사용합니다.

List<Student> maleList = totalList.stream()
	.filter(s->s.getSex().equals("남"))
    .toList();

 


public class Student{
	private String name;
	private String sex;
	private int score;
	
	public Student(String name, String sex, int score) {
		this.name = name;
		this.sex = sex;
		this.score = score;
	}
	
	public String getName() {return name;}
	public String getSex() {return sex;}
	public int getScore() {return score;}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Map;import java.util.stream.Collector;
import java.util.stream.Collectors;

public class CollectExam {

	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());

//		java 16버전 이후
//		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}

 


요소 그룹핑

  • collectors.groupingBy() 메소드에서 얻은 Collector를 collect() 메소드를 호출할 때 제공합니다.
  • groupingBy()는 Function을 이용하여 T를 K로 매핑하고, K를 키로해 List<T>를 값으로 갖는 Map 컬렉션을 생성합니다.

 


다음은 "남", "여"를 키로 설정하고 List<Student>를 값으로 갖는 Map 을 생성하는 코드입니다.

Map<String, List<Student>> map = totalList.stream()
	.collect(
    	Collectors.groupingBy(s->s.getSex())	//그룹핑 키 리턴
    );

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectExam {

	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, 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를 가질 수 있습니다.


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectExam {

	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}

 


동시성과 병렬성

  • 동시성 : 멀티 작업을 위해 멀티 스레드가 하나의 코어에서 번갈아 가며 실행하는 것
  • 병렬성 : 멀티 작업을 위해 멀티 코어를 각각 이용해서 병렬로 실행하는 것

  • 데이터 병렬성 : 전체 데이터를 분할해서 서브 데이터 셋으로 만들고 이 서브 데이터 셋들을 병렬 처리해서 작업을 빨리 끝내는 것
  • 작업 병렬성 : 서로 다른 작업을 병렬 처리 하는 것 

포크조인 프레임 워크

  • 포크(Fork) 단계 : 전체요 소들을 서브 요소셋으로 분할하고, 각각의 서브 요소셋을 멀티 코어에서 병렬로 처리 합니다.
  • 조인(Join) 단계 : 서브 결과를 결합해서 최종 결과를 만들어 냅니다.

  • 포크조인 프레임워크는 ExecutorService의 구현 객체인 ForkJoinPool을 사용해서 작업 스레드를 관리합니다.

 


병렬 스트림 사용

  • 자바 병렬 스트림은 백그라운드에서 포크조인 프레임워크가 사용하므로 병렬 처리가 용이합니다.
  • parallelStream() 메소드는 컬렉션(List, Set)으로부터 병렬 스트림을 바로 리턴합니다.
  • parallel() 메소드는 기존 스트림을 병렬 처리 스트림으로 변환 합니다.


import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

public class ParallelExam {

	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: 50.00118058, 일반 스트림 처리 시간: 155528300ns
avg: 50.00118058, 병렬 스트림 처리 시간: 69278400ns

병렬 처리 성능에 영향을 미치는 요인

  • 요소의 수와 요소당 처리시간
  • 스트림 소스의 종류
  • 코어(Core)의 수

 

728x90

'JAVA > Java 기초' 카테고리의 다른 글

네트워크 기초(IP, TCP, UDP).java  (0) 2023.01.31
기본 타입 스트림.java  (0) 2023.01.31
입출력 스트림-2.java  (0) 2023.01.30
입출력 스트림.java  (0) 2023.01.30
Optional 클래스.java  (0) 2023.01.30