프로그래밍 언어/Java

[Stream] 집계, Optional 클래스, 커스텀 집계(reduce)

코딩하는흑구 2019. 9. 19. 11:24
기본 집계 메소드

- 최종 처리 기능

- 요소들을 처리해서 카운팅, 합계, 평균값, 최대값, 최소값 등 하나의 값으로 산출

- 대량의 데이터를 가공해서 축소하는 리덕션.

 

기본 집계 메소드 예제

package Stream.middleprocess.zipgue;

import java.util.Arrays;

public class AggregateExample {
	public static void main(String[] args) {
		//배열선언
        int[] temp = new int[] {
				1,2,3,4,5
		};
        //카운트
		long count = Arrays.stream(temp).filter(n -> n%2 ==0)
		.count();
		System.out.println("2의 배수 개수 : "+count);
		
        //합계
		long sum = Arrays.stream(temp).filter(n->n%2==0)
		.sum();
		System.out.println("2의 배수의 합 : "+sum);
		
        //평균
		double avg = Arrays.stream(temp)
				.filter(n->n%2==0)
				.average()
				.getAsDouble();
		System.out.println("2의 배수의 평균 : "+avg);
		
        //최대
		int max = Arrays.stream(temp)
				.filter(n -> n%2==0)
				.max()
				.getAsInt();
		System.out.println("2의 배수 최대값 : "+max);
		
        //최소
		int min = Arrays.stream(temp)
				.filter(n->n%2==0)
				.min()
				.getAsInt();
		System.out.println("2의 배수 최소값 : "+min);
		
	}
}

Optional 클래스

- 자바 8에서 추가한 java.util 패키지의 Optional, OptionalDouble, OptionalInt, OptionalLong 클래스 타입.

- 값 기반 클래스(value-based class) 

- 값을 얻기 위해서는 get(), getAsDouble(), getAsInt(), getAsLong() 메소드를 호출하면 됨.

- 단순히 집계 값만 저장하는 것이 아닌, 집계값이 존재하지 않을 경우 디폴트 값을 설정할 수 있고, 집계값을 처리하는 Consumer도 등록할 수 있다.

 

Optional 클래스의 메소드들
리턴 타입 메소드(매개변수) 설명
boolean isPresent() 값이 저장되어 있는지 여부

T

double

int

long

orElse(T)

orElse(double)

orElse(int)

orElse(long)

값이 저장되어 있지 않을 경우 디폴트 값 지정
void

ifPresent(Consumer)

ifPresent(DoubleConsumer)

ifPresent(IntConsumer)

ifPresent(LongConsumer)

값이 저장되어 있을 경우 Consumer에서 처리

 

예제 : 스트림에 값이 없는 경우.

List<Integer> list = new ArrayList<>();
		
double avg = list.stream()
    .mapToInt(Integer::intValue)
	.peek(p -> System.out.println(p))
	.average()
	.getAsDouble();

- list 컬렉션에는 아무것도 들어있지 않다. 그렇게 stream을 반환하면 역시 아무것도 없는 스트림이 반환된다.

- 그렇게 되면 average() 메소드를 호출후 그 값을 꺼내려(getXXX() 메소드 호출) 했을 때 어떻게될까????

   -> 값이 없으면 에러가남.( java.util.NoSuchElementException)

- 그렇기 때문에 예외를 피하는 경우 3가지가 있다.

 

예제 : Optional 클래스를 사용할 때 예외를 피하는 3가지

import java.util.*;

public class OptionalException {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();

		//방법 1
		OptionalDouble optional = list.stream()
				.mapToInt(Integer::intValue)
				.average();
		if(optional.isPresent())
			System.out.println("방법1_평균 : "+optional.getAsDouble());
		else
			System.out.println("방법1_평균 : "+0.0 );
		
		//방법2
		double avg = list.stream()
				.mapToInt(Integer::intValue)
				.average()
				.orElse(0.0);
		System.out.println("방법2_평균 : "+avg);
				
		//방법3 : 출력결과 안나옴. 없으면 행위자체를 안하는거나 마찬가지
		list.stream()
			.mapToInt(Integer::intValue)
			.average()
			.ifPresent(a->System.out.println("방법3_평균 : "+a));
	}
}

 방법 1. average까지 계산 후 isPresent() 메소드를 통해 값이 있는지 확인하여 분기한다

 방법 2. orElse() 메소드를 이용해서 디폴트 값을 설정하여 값을 불러온다.

 방법 3. ifPresent() 메소드를 이용하여 값이 있는 경우만 람다식을 실행한다.

 

커스텀 집계

- 기본 집계 메소드 외에도 프로그램화해서 다양한 집계 결과물을 만들 수 있도록 reduce() 제공.

-  reduce(default value, XXXOperator) 

import java.util.*;
import Stream.Student;
public class ReductionExample {
	public static void main(String[] args) {
		List<Student> studentList = Arrays.asList(
			new Student("가나다", 30),
			new Student("라마바", 20),
			new Student("사아자", 10)
		);
		//studentList = new ArrayList<Student>();
		
		int sum1 = studentList.stream().mapToInt(Student::getScore).sum();
		
		int sum2 = studentList.stream().map(Student::getScore)
				.reduce((a,b) -> a+b).get(); //스트림이 없을 경우엔 NoSuchElementException 
		
		int sum3 = studentList.stream().map(Student::getScore).reduce(0, (a,b)->a+b);
		
		System.out.println("sum1 : "+sum1);
		System.out.println("sum2 : "+sum2);
		System.out.println("sum3 : "+sum3);
	}
}