본문 바로가기

일/JAVA

java16.stream.aggregate

/*

집계(count(),sum(),average(),max(), min()...)

집계(aggregate)는 최종처리기능으로 요소들을 처리해서 갯수, 합계, 평균, 최대/최소값 등과 같이

하나의 값으로 집계하는 기능을 말한다.

 

집계는 대용량의 데이터를 가공해서 축소하는 reduction이라고 볼 수 있다. 스트림이 제고하는

기본집계 메서드는

1. count() : 요소의 갯수를 long타입으로 리턴

2. findFirst() : 첫번째 요소를 OptionalXXX타입으로 리턴

3. max(Comparator<T>) : 요소중 최대요소를 OptionalXXX타입으로 리턴

4. min(Comparator<T>) : 요소중 최소요소를 OptionalXXX타입으로 리턴

5. average() : 요소들의 평균값을 OptionalDouble타입으로 리턴

6. sum() : 요소들의 합계를 int, long, double 중 한개의 타입으로 리턴

 

이 집계요소에서 리턴하는 OptionalXXX클래스는 Java에서 추가한 java.utill 패키지의 Optional,

OptionalInt, OptionalLong, OptionalDouble클래스 타입을 말한다. 이들 객체에서 값을 얻기 위해서는

각각 get(), getAsDouble(), getAsInt(), getAsLong()메서드를 호출하면 된다.

 

이 OptionalXXX클래스들은 저장하는 값의 타입만 다를뿐 제공하는 기능은 거의 유사하다.

Optional 클래스는 단순히 집계만 제공하는 것이 아니라 집계값이 존재하지 않을 경우 디폴트값을 설정할 수 있고

집계값을 처리하는 Consumer클래스도 등록할 수 있다.

*/

public class AggregateMain {

 

public static void main(String[] args) {

int[] int_arr = {1,2,3,4,5};

// 1. count

long count = Arrays.stream(int_arr).filter(n->n%2==0).count();

System.out.println("2의 배수의 갯수= " +count);

 

// 2. sum

int result = Arrays.stream(int_arr).filter(n->n%2==0).sum();// 리턴타입을 int, long, double로 정의가능

System.out.println("2의 배수의 합계= "+result);

 

// 3. average

OptionalDouble avg = Arrays.stream(int_arr).filter(n->n%2==0).average();

System.out.println(Arrays.stream(int_arr).filter(n->n%2==0).average().getClass());

System.out.println("2배수의 평균= "+avg.getAsDouble());

System.out.println("2배수의 평균= "+Arrays.stream(int_arr).filter(n->n%2==0).average().getAsDouble());

double result1 = Arrays.stream(int_arr).filter(n->n%2==0).average().getAsDouble();

System.out.println("2배수의 평균 = " +result1);

System.out.println();

 

// 4. max

System.out.println(Arrays.stream(int_arr).filter(n->n%2==0).max().getClass());

System.out.println("2배수 중 최대값= "+Arrays.stream(int_arr).filter(n->n%2==0).max().getAsInt());

System.out.println();

 

// 5. min

System.out.println(Arrays.stream(int_arr).filter(n->n%2==0).min().getClass());

System.out.println("2배수 중 최소값= "+Arrays.stream(int_arr).filter(n->n%2==0).min().getAsInt());

System.out.println();

 

// 6. findFirst

System.out.println(Arrays.stream(int_arr).filter(n->n%2==0).findFirst().getClass());

System.out.println("2배수 중 첫번째값= "+Arrays.stream(int_arr).filter(n->n%2==0).findFirst().getAsInt());

System.out.println();

}

}

public class OptionalMain {

 

public static void main(String[] args) {

 

// List<Integer> list = new ArrayList<>();

List<Integer> list = Arrays.asList(1,2,3,4,5); // 차이비교

 

 

try {

double result = list.stream().mapToInt(Integer::valueOf).average().getAsDouble();

} catch (Exception e) {

System.out.println("try~catch : 저장된 값이 없습니다");

}

// try~catch이외의 예외처리방법(1) - isPresent(), 요소존재여부를 리턴

OptionalDouble optional = list.stream().mapToInt(Integer::valueOf).average();

if(optional.isPresent()) {

System.out.println("평균값= "+optional.getAsDouble());

}else {

System.out.println("isPresent()메서드 : 저장된 요소가 없습니다");

}

// try~catch이외의 예외처리방법(2) - orElse(), 요소가 없을 경우 default값처리

double result = list.stream().mapToInt(Integer::valueOf).average().orElse(1000);

System.out.println("orElse의 기본값 처리: "+ result);

// try~catch이외의 예외처리방법(3) - ifPresent()

list.stream()

.mapToInt(Integer::intValue)

.average()

.ifPresent(a->System.out.println("ifPresent:평균값= "+a)); // 값이 있으면 처리, 없으면 동작하지 않음

}

}

public class ReductionMain {

 

public static void main(String[] args) {

// map -> reduction ->MapAndReduce

List<Student> list = Arrays.asList(

new Student("홍길동",90),

new Student("홍길순",98),

new Student("홍길자",95)

);

// 1. 합계구하기(1) - sum()

int sum = list.stream().mapToInt(Student::getScore).sum();

System.out.println("합계구하기(1)-sum()= "+sum);

// 2. 합계구하기(2) - reduce(BinaryOperator), 예외처리를 하지 않음

// reduce의 리턴타입은 OptionalInt

sum = list.stream().mapToInt(Student::getScore).reduce((a,b)->a+b).getAsInt();

System.out.println("합계구하기(2)-reduce()= "+sum);

// 3. 합계구하기(3) - reduce(int, BinaryOperator), 예외처리를 함

sum = list.stream().mapToInt(Student::getScore).reduce(0,(a,b)->a+b);

System.out.println("합계구하기(3)-reduce()= "+sum);

}

}

 

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;

}

}

/*

수집(collect())

스트림은 요소들을 필터링 or 매핑한 후에 요소들을 수집하는 최종처리메서드인 collect()를 제공한다.

Stream의 collect(Collector<T,A,R> collector)메서드는 필터링 or 매핑된 요소들을 새로운 컬렉션타입으로

수집하고 그 결과인 Collection(List, Set, Map)으로 리턴한다.

 

매개값인 Collectors는 어떤 요소를 어떤 컬렉션타입으로 수집할 것인지를 정의한다. Collectors의 타입

파라미터인 T는 스트림의 요소, A는 누적기(accumulator), R은 스트림요소들이 저장될 컬렉션을 의미한다.

 

리턴값인 Collectors에는 Collectors<T, ?, R>처럼 누적기(A)가 ?로 되어 있는 경우도 있는데 이 의미는

Collector가 T(스트림의 요소)를 R(컬렉션)에 저장하는 방법을 알고 있어서 A가 필요없다는 의미이다.

 

Map과 ConcurrentMap의 차이점은 Map은 쓰레드에 안전하지 않고 ConcurrentMap은 쓰레드에 안전하다.

따라서 멀티쓰레드환경에서는 Map보다는 ConcurrentMap을 사용하는 것이 좋다.

*/

public class CollectMain1 {

 

public static void main(String[] args) {

// Collectors에는 toList(), toSet(), toMap()메서드가 있다.

// 이 메서드로 컬렉션데이터타입으로 리턴할 수가 있다.

List<Student> list = Arrays.asList(

new Student("홍길동", 0, Student.Gender.MALE),

new Student("홍길순", 0, Student.Gender.FEMALE),

new Student("홍길녀", 0, Student.Gender.FEMALE),

new Student("홍길상", 0, Student.Gender.MALE));

// 1. Collector로 남학생만의 List컬렉션을 생성

List<Student> 남학생 = list.stream()

.filter(s->s.getGender() == Student.Gender.MALE)

.collect(Collectors.toList());

System.out.println(남학생.getClass());

남학생.stream().forEach(s->System.out.print(s.getName()+", "));

System.out.println();

 

// 2. Collector로 여학생만의 Set(HashSet)컬렉션을 생성

Set<Student> 여학생 = list.stream()

.filter(s->s.getGender() == Student.Gender.FEMALE)

.collect(Collectors.toSet());

System.out.println(여학생.getClass());

여학생.stream().forEach(s->System.out.print(s.getName()+", "));

System.out.println();

 

// toCollection(컬렉션타입)

여학생 = list.stream()

.filter(s->s.getGender()==Student.Gender.FEMALE)

.collect(Collectors.toCollection(HashSet::new)); // 생성자호출

여학생.stream().forEach(s->System.out.print(s.getName()+", "));

System.out.println();

 

여학생 = list.stream()

.filter(s->s.getGender()==Student.Gender.FEMALE)

.collect(Collectors.toCollection(HashSet<Student>::new)); // 생성자호출

여학생.stream().forEach(s->System.out.print(s.getName()+", "));

System.out.println();

 

여학생 = list.stream()

.filter(s->s.getGender()==Student.Gender.FEMALE)

.collect(Collectors.toCollection(()->{return new HashSet<Student>();})); // 생성자호출

여학생.stream().forEach(s->System.out.print(s.getName()+", "));

System.out.println();

}

}

 

class Student {

public enum Gender{MALE, FEMALE};

public enum City{SEOUL, BUSAN};

 

private String name;

private int score;

private Gender gender;

private City city;

 

public Student(String name, int score, Gender gender) {

this.name = name;

this.score = score;

this.gender = gender;

}

public Student(String name, int score, Gender gender, City city) {

this.name = name;

this.score = score;

this.gender = gender;

this.city = city;

}

public String getName() {

return name;

}

public int getScore() {

return score;

}

public Gender getGender() {

return gender;

}

public City getCity() {

return city;

}

}

/*

사용자 정의 컨테이너에 수집하기

List, Set, Map 컬렉션을 사용하는것ㅇ ㅣ아니라 사용자가 만든 컨테이너(즉, 사용자가 정의한 컬렉션)로

stream 저장하는 방법을 실습

 

stream은 요소들을 필터링 or 매핑을 해서 사용자가 정의한 컨테이너에 요소들을 저장할 수 있도록

collect()메서드를 이용해서 사용자정의 컨테이너에 저장할 수 있다.

*/

public class CollectMain2 {

 

public static void main(String[] args) {

 

List<Student> list = Arrays.asList(

new Student("홍길동", 0, Student.Gender.MALE),

new Student("홍길순", 0, Student.Gender.FEMALE),

new Student("홍길녀", 0, Student.Gender.FEMALE),

new Student("홍길상", 0, Student.Gender.MALE));

 

MaleStudent 남학생 = list.stream()

.filter(s->s.getGender()==Student.Gender.MALE)

// .collect(()-> new MaleStudent(),(m, t)->m.accumulate(t),(m1, m2)->m1.combine(m2));

.collect(MaleStudent::new, MaleStudent::accumulate,MaleStudent::combine);

남학생.getList().stream().forEach(s->System.out.print(s.getName()+", "));

// 남학생을 사용자 컨테이너(MaleStudent)에 저장(수집)하는 과정

// 1) Stream<Student> tot_stream = list.stream();

// 2) Stream<Student> mal_stream = tot_stream.filter(s->s.getGender()==Student.Gender.MALE)

 

// 3) Supplier<MaleStudent> supplier = () -> new MaleStudent()

// 4) BiConsumer<MaleStudent, Student> accumulator = (m, t) -> m.accumulate(t)

// 5) BiConsumer<MaleStudent,MaleStudent> combine = (m1, m2)->m1.combine(m2)

}

}

 

class MaleStudent{

// 남학생들만 저장할 컨테이너

private List<Student> list; // 남학생들의 요소만 저장할 컬렉션

 

public MaleStudent() {

this.list = new ArrayList<Student>();

System.out.println("["+Thread.currentThread().getName()+"]생성자를 호출한 쓰레드");

}

// 요소들을 저장할 메서드

public void accumulate(Student student) {

this.list.add(student);

System.out.println("["+Thread.currentThread().getName()+"]accumulate()를 호출한 쓰레드");

}

// 두 MaleStudent를 결합하는 매서드(병렬처리시에만 처리)

public void combine(MaleStudent other) {

this.list.addAll(other.getList());

System.out.println("["+Thread.currentThread().getName()+"]accumulate()를 호출한 쓰레드");

}

public List<Student> getList() {

return this.list;

}

}

/*

요소를 그룹핑해서 저장하기

 

collect()메서드는 단순히 요소를 수집(저장)하는 기능이외에 컬렉션의 요소들을 특정값으로 그루핑해서

Map 객체를 생성하는 기능을 제공한다. collect()를 호출할 때 Collector의 groupingBy(), groupingByConcurrent()가

리턴하는 Collector를 매개값으로 대입하면 된다.

 

groupingBy()는 쓰레드에 안전하지 않는 Map을 생성하지만 groupingByConcurrent()는 쓰레드에 안전한

ConcurrentMap을 생성한다.

*/

public class CollectMain3 {

 

public static void main(String[] args) {

 

List<Student> list = Arrays.asList(

new Student("홍길동", 0, Student.Gender.MALE, Student.City.SEOUL),

new Student("홍길순", 0, Student.Gender.FEMALE, Student.City.BUSAN),

new Student("홍길녀", 0, Student.Gender.FEMALE, Student.City.SEOUL),

new Student("홍길상", 0, Student.Gender.MALE, Student.City.BUSAN));

// 1. 성별 그룹핑

Map<Student.Gender, List<Student>> by_gender = list.stream().collect(Collectors.groupingBy(Student::getGender));

System.out.println(by_gender.getClass());

System.out.println();

 

System.out.println("[남학생]");

System.out.println(by_gender.get(Student.Gender.MALE).getClass());

by_gender.get(Student.Gender.MALE).stream().forEach(s->System.out.println(s.getName()+","));

System.out.println("[여학생]");

by_gender.get(Student.Gender.FEMALE).stream().forEach(s->System.out.println(s.getName()+","));

System.out.println("\n---------------------------------------------------");

 

// 2. 도시별 그룹핑

Map<Student.City, List<Student>> by_city = list.stream().collect(Collectors.groupingBy(Student::getCity));

System.out.println("서울: ");

by_city.get(Student.City.SEOUL).stream().forEach(s->System.out.println(s.getName()+","));

System.out.println("부산: ");

by_city.get(Student.City.BUSAN).stream().forEach(s->System.out.println(s.getName()+","));

System.out.println("\n---------------------------------------------------");

 

// 3. Collectors.groupingBy(그룹조건(Function), 맵핑(집계))

// Collectors.groupingBy()메서드는 그룹핑 후에 맵핑이나 집계(평균, 카운팅, 합계...)를 할 수 있도록

// 2번째 매개변수로 Collectors객체를 정의할 수 있다.

Map<Student.City, List<String>> by_city1 = list.stream()

.collect(Collectors.groupingBy(Student::getCity, Collectors.mapping(Student::getName, Collectors.toList())));

System.out.println("[서울]");

by_city1.get(Student.City.SEOUL).stream().forEach(s->System.out.print(s+", "));

System.out.println("\n[부산]");

by_city1.get(Student.City.BUSAN).stream().forEach(s->System.out.print(s+", "));

}

 

}

 

/*

병렬처리

병렬처리(Parallel Operation)란 멀티코어 CPU환경에서 하나의작업을 분할해서 각각의 코어가

병렬적으로 처리하는 것을 말한다. 병렬처리의 목적은 작업시간을 줄이기 위한 것이다.

Java8버전부터 요소를 병렬처리할 수 있도록 하기 위해서 병렬처리스트림을 제공하기 때문에

컬렉션, 배열의 전체요소를 처리하는 시간을 줄여준다.

 

동시성(Concurrency)과 병렬성(Paralleism)

멀티쓰레드는 동시성 또는 병렬성으로 실행되기 때문에 이 둘이 멀티 쓰레드의 동작방식이라는 점에서는

동일하나 각각 다른 목적을 가지고 있다.

 

동시성은 멀티작업을 위해 멀티쓰레드가 번갈아가면서 실행하는 것을 말하고 병렬성은 멀티작업을

위해 멀티코어를 이용해서 동시에 실행하는 것을 말한다.

*/

public class ParallelismMain {

 

public static void main(String[] args) {

List<Student> list = Arrays.asList(

new Student("홍길동", 90, Student.Gender.MALE, Student.City.SEOUL),

new Student("홍길순", 88, Student.Gender.FEMALE, Student.City.BUSAN),

new Student("홍길녀", 89, Student.Gender.FEMALE, Student.City.SEOUL),

new Student("홍길상", 91, Student.Gender.MALE, Student.City.BUSAN));

MaleStudent 남학생 = list.parallelStream()

.filter(s->s.getGender()==Student.Gender.MALE)

.collect(MaleStudent::new, MaleStudent::accumulate,MaleStudent::combine);

남학생.getList().stream().forEach(s->System.out.println(s.getName()+", "));

}

 

}

class Student {

public enum Gender{MALE, FEMALE};

public enum City{SEOUL, BUSAN};

 

private String name;

private int score;

private Gender gender;

private City city;

 

public Student(String name, int score, Gender gender) {

this.name = name;

this.score = score;

this.gender = gender;

}

public Student(String name, int score, Gender gender, City city) {

this.name = name;

this.score = score;

this.gender = gender;

this.city = city;

}

public String getName() {

return name;

}

public int getScore() {

return score;

}

public Gender getGender() {

return gender;

}

public City getCity() {

return city;

}

 

}

 

class MaleStudent{

// 남학생들만 저장할 컨테이너

private List<Student> list; // 남학생들의 요소만 저장할 컬렉션

 

public MaleStudent() {

this.list = new ArrayList<Student>();

System.out.println("["+Thread.currentThread().getName()+"]생성자를 호출한 쓰레드");

}

// 요소들을 저장할 메서드

public void accumulate(Student student) {

this.list.add(student);

System.out.println("["+Thread.currentThread().getName()+"]accumulate()를 호출한 쓰레드");

}

// 두 MaleStudent를 결합하는 매서드(병렬처리시에만 처리)

public void combine(MaleStudent other) {

this.list.addAll(other.getList());

System.out.println("["+Thread.currentThread().getName()+"]accumulate()를 호출한 쓰레드");

}

public List<Student> getList() {

return this.list;

}

}

' > JAVA' 카테고리의 다른 글

java16.stream.pipeline  (0) 2023.05.30
java16.stream.intro  (0) 2023.05.30
java15.collection  (0) 2023.05.26
java14.lambda 람다람다  (0) 2023.05.26
java13.generic  (1) 2023.05.26