본문 바로가기

Java/Study

Ch16 스트림과 병렬처리

 

 

오리지널 스트림

중간처리 스트림 : 필터링 처리 중간 스트림, 매핑 처리 중간 스트림 (루핑)

최종처리 :스트림  집계 처리 결과물 (평균)

 

- 오리지널 스트림과 최종 처리 스트림은 최소 하나는 존재해야 한다.

- 결과는 한번만 볼 수 있다.

 

Stream을 만들고 사용하는 법

① 컬렉션, 배열로 자료를 만든다

     List<String> list = Arrays.asList("홍길동", "신용권", "김자바);

 

② 컬렉션, 배열을 오리지널 스트림으로 만든다

     Stream<list> stream = list.stream();

 

최종스트림 : forEach로 사용(매개변수는 람다식)한다

      stream.forEach( a -> System.out.println(a) );

      stream.forEach( a -> System.out.println(a) );  //위에서 이미 최종 스트림을 소비했기 때문에 재사용할 수 없다. (다시 쓰려면 2로 돌아감)


스트림의 특징

01 람다식으로 요소 처리 코드를 제공한다

 

Stream이 제공하는 대부분의 요소 처리 메소드는 함수적 인터페이스 매개 타입을 가진다따라서 람다식 또는 메소드 참조를 이용해서 요소 처리 내용을 매개값으로 전달할 수 있다

 

예제

더보기
public class LambdaExpressionsEx {
	public static void main(String[] args) {
		//1단계 : 컬렉션이나 배열로 만든다
		List<Student> list = Arrays.asList(new Student("홍길동",90), new Student("신용권", 92));
		//2단계 : 오리지널 스트림으로 만든다
		Stream<Student> stream = list.stream();
		//3단계 최종 스트림으로 만들어 결과를 낸다.
		stream.forEach(s->{ //메소드 호출 따로 하지 않고 forEach에서 알아서 소비
			String name = s.getName();
			int score = s.getScore();
			System.out.println(name + " - " + score);
		}); //반복해서 출력해라
	}
}

class Student{
	private String name;
	private int score;
	
	Student(String name, int score){
		this.name = name;
		this.score = score;
	}
	public String getName() {return name;}
	public void setName(String name) {this.name = name;}
	public int getScore() {return score;}
	public void setScore(int score) {this.score = score;}
}

02 내부 반복자를 사용하여 병렬 처리가 쉽다

 

외부반복자는 개발자가 직접 컬렉션의 요소를 반복해서 가져오는 코드 패턴으로, index를 이용하는 for문이나 Iterator를 이용하는 while문

반면 내부반복자는 컬렉션 내부에서 요소들을 반복시키고,

개발자는 람다식으로 요소당 처리해야 할 코드만 제공하는 코드 패턴개발자가 요소 처리 코드에만 집중할 수 있게 해준다.

 

무엇보다, 요소들의 반복 순서를 변경하거나 멀티 코어 CPU를 활용하여 병렬작업이 가능

parallelStream() 를 사용하여 스트림을 만들 경우 우리 눈엔 stream()을 쓴 것과 별 차이 없어보이지만 컴퓨터는 내부적으로 병렬처리한다.

 

더보기
//786page 병렬처리 스트림 연습
public class ParallelEx {
	public static void main(String[] args) {
		//1단계 컬렉션 준비
		List<Integer> list =Arrays.asList(new Integer(10), new Integer(20), new Integer(30));
		
		//2단계 오리지널 스트림 만들기
		//순차처리 - 싱글 스레드 처리 1개 코어(cpu)에게 처리요청하기
		Stream<Integer> stream = list.stream();
		stream.forEach(ParallelEx :: print); //메소드호출만 하는 경우 클래스명::메소드명으로 줄일 수 있다.
		System.out.println();
		
		//병렬처리
		Stream<Integer> parallelStream = list.parallelStream();
		parallelStream.forEach(ParallelEx::print);
		System.out.println();
	}
	public static void print(Integer i) {
		System.out.println(i + " : " + Thread.currentThread().getName());
	}
}

03 오리지널스트림.중간스트림.최종스트림을 하나의 코드로 만들 수 있다

 

중간처리에서 매핑, 필터링, 정렬 수행

최종 처리에서는 반복, 카운팅, 평균, 총합 등의 집계 처리를 수행

 

list.stream().map(Student::getName).count();

 

public class MapAndReduceEx {
	public static void main(String[] args) {
		List<Student> list = Arrays.asList(new Student("홍길동", 10), new Student("신용권",20), new Student("유미선", 30));
//		double avg = list.stream().mapToInt(Student::getScore).average().getAsDouble();
					//오리지널스트림 > 중간스트림 : 번지를 넘겨 메소드 호출(10,20,30) > 평균구하기 > Double로 만들기 
		System.out.println(list.stream().mapToInt(Student::getScore).average().getAsDouble());
										//이름-점수 쌍을 넘겨주고 점수를 리턴
		System.out.println(list.stream().mapToInt(Student :: getScore).sum());
		//이름 출력하기
		list.stream().map(Student :: getName).forEach(a-> System.out.println(a));
		long cctN = list.stream().map(Student :: getName).count();
		System.out.println(cctN);
	}
}

class Student{
//	private String grade;
	private String name;
	private int score;
	public Student(String name, int score) {
		this.name = name;
		this.score = score;
	}
	public String toString() {
		return name;
	}
	public String getName() {return name;}
	public void setName(String name) {this.name = name;}
	public int getScore() {	return score;}
	public void setScore(int score) {this.score = score;}
}

 


 

 스트림의 종류 

 

01 컬렉션으로부터 스트림 얻기

Arrays.asList().stream()

		List<Student> studentList = Arrays.asList(
		new Student("홍길동", 10),new Student("신용권", 20),new Student("유미선", 30));
		Stream<Student> stream = studentList.stream();
		stream.forEach(s-> System.out.println(s.getName()));

 

02 배열로부터 스트림 얻기

Arrays.stream(배열)

Arrrays.stream(new int[]())

 

		String[] strArray = {"홍길동", "신용권", "김미나"};
		Stream<String> strStream = Arrays.stream(strArray);
		strStream.forEach(a->System.out.print(a+ ","));
		System.out.println();

 

 

03 숫자 범위로부터 스트림 얻기

IntStream.rangeClosed(startInclusive, endInclusive)

 

	public static int sum;
	public static void main(String[] args) {
		IntStream stream = IntStream.rangeClosed(1, 100);
		stream.forEach(a -> sum += a);
		System.out.println("총합 : " + sum);
	}

 

 

04 파일로부터 스트림 얻기

Path path = Paths.get("경로")

 

① Files.lines()메소드 이용

Files.lines(path)

 

② BufferedReader의 lines() 메소드 이용

new BufferedReader(new FileReader(path.toFile())).lines();

 

- 18장에서 학습함 -

		Path path = Paths.get("c:/temp/test1.txt");
		Stream<String> stream;
		
		//Files.lines() 메소드 이용
		stream = Files.lines(path);
		stream.forEach(System.out::println);
		System.out.println();
		
		//BufferedReader의 lines() 메소드 이용 (오류남)
		File file = path.toFile();
		FileReader fileReader = new FileReader(file);
		BufferedReader br = new BufferedReader(fileReader);
		stream = br.lines();
		stream.forEach(System.out :: println);

 

05 디렉토리로부터 스트림 얻기

Files.list(Paths.get("경로"))

 

 

Files의 정적메소드 list()를 이용해서 디렉토리의 내용을 스트림을 통해 읽고 콘솔에 출력

 

	public static void main(String[] args) throws IOException {
		Path path = Paths.get("C:\\Users\\jeey0\\Desktop\\학원\\study\\JAVA_STUDY2\\Ch16\\src");
		Stream<Path> stream = Files.list(path);
		//p:서브 디렉토리 혹은 파일에 해당하는 Path객체
		stream.forEach(p->System.out.println(p.getFileName()));
	}

 


 

 스트림 파이프라인 

 

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

리덕션을 효과적으로 할 수 있도록 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리 필요

최종처리는 합계, 평균, 카운팅, 최대값, 최소값 등을 말함

 

스트림 소스(컬렉션, 배열,파일) → 오리지널 스트림 → 중간처리(필터링 처리 중간 스트림→매핑 처리 중간스트림)→최종처리 →결과

 

 

 

예제

더보기
public class StreamPipelineExample {
	public static void main(String[] args) {
		List<Member> list = Arrays.asList(
			new Member("홍길동", Member.MALE, 30),
			new Member("김나리", Member.FEMALE, 20),
			new Member("신용권", Member.MALE, 45),
			new Member("박수미", Member.FEMALE, 27)
		);
		double ageAvg = list.stream().filter(m->m.getSex()==Member.MALE).mapToInt(Member::getAge).average().getAsDouble();
		System.out.println("남자 평균 나이: " + ageAvg );
	}
}

class Member{
	static int MALE = 0;
	static int FEMALE = 1;
	private String name;
	private int sex;
	private int age;
	
	Member(String name, int sex, int age){ this.name = name; this.sex = sex; this.age = age;}
	int getSex() { return sex;}
	int getAge() { return age;}
}

 

오리지널 스트림 만들기 예제

1. 100~800까지 스트림으로 만들어 합 구하고 출력

 

더보기
		//100~800까지 스트림으로 만들고 합을 구해서 출력
		IntStream int100To800 = IntStream.rangeClosed(100, 800); //range는 endExlusive
		int100To800.forEach(i->System.out.println(i));
		
		IntStream int100To800Again = IntStream.rangeClosed(100, 800); //range는 endExlusive
		int sum100To800 = int100To800Again.sum();
		System.out.println(sum100To800);

 

 

2. double 배열로 값 5개 넣고 최대값,최소값 구하기

더보기
		//double 배열로 값을 5개 넣기 ->오리지널 스트림 ->최대값
		double[] doubleArr = {1.2, 3.5, 5.1, 5.9, 28.3};
		//1. Arrays.stream(double[] d) 방법2 DobuleStream.of(double[])
		DoubleStream dS = Arrays.stream(doubleArr);
		DoubleStream dS2 = DoubleStream.of(doubleArr);
		
		double maxValue = Arrays.stream(doubleArr).max().getAsDouble();
		System.out.println("최대값 :" + maxValue);
		System.out.println("최소값: " + dS2.min().getAsDouble());

 

3. 정수의 랜덤수를 스트림으로 만들어서 출력하기

ints() : int로 반환

limit() : 제한

더보기
new Random().ints().limit(10).forEach(System.out::println);
//혹은
IntStream r = new Random().ints();
r.limit(10).forEach(System.out::println);
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

//793page
public class FromFileContentEx {

   public static void main(String[] args) throws IOException {
      //경로 지정 
      Path path = Paths.get("c://temp//test1.txt");
      Path path1 = Paths.get("c://temp");
      
      Stream<String> stream;
      
       //경로의 파일을 읽어서 스트림으로 만들기
      stream = Files.lines(path);
      stream.forEach(System.out::println);
      
      System.out.println("------------------");
      Stream<Path> stream1 =  Files.list(path1);
      stream1.forEach(System.out::println);
      System.out.println("=================");
      
      File file = path.toFile();
      FileReader fileReader =new FileReader(file);
      BufferedReader   br = new BufferedReader(fileReader);
      stream = br.lines();
      stream.forEach(System.out::println);
   }
}

필터링 [ distinct(), filter() ]

 

distinct() : 중복제거

                     Object.equals(Object)가 true면 동일객체로 판단하고 중복 객체 (동등비교)

 

filter() : 조건 필터링

               매개값은 Predicate, IntPredicate, LongPredicate, DoublePredicate

               매개값으로 주어진 Predicate가 true를 리턴하는 요소만 필터링 (동일값일 경우 중복 제거)

               startsWith() : 해당 문자열로 시작하는 요소만 걸러줌

 

예제

더보기
public class FilterExample {
	public static void main(String[] args) {
		List<String> names = Arrays.asList("홍길동", "신용권", "감자바", "신용권","신민철");
		names.stream().distinct().forEach(System.out::println); //중복제거
		System.out.println("====");
		names.stream().filter(n->n.startsWith("신")).forEach(System.out::println); //신씨만 출력
		System.out.println("====");
		names.stream().distinct().filter(n->n.startsWith("신")).forEach(System.out::println); //중복제거 후 신씨만 출력
	}
}

 


매핑 [ flatMapXXX(), mapXXX(), asXXXStream(), boxed() ]

 

flatMapXXX()메소드

요소를 대체하는 복수 개의 요소들로 구성된 새로운 스트림을 리턴한다

원래 스트림 : B A → 쪼갠다 → 새로운 스트림 : B2 B1 A2 A1

 

 

inputList.stream().flatMap(data->Arrays.stream(data.split("구분자"))).forEach(System.out:println);

스트림으로 만든다 → 매핑 메소드 ( →람다식으로 데이터를 split()하여 쪼개고, 다시 Arrays.stream()하여 스트림으로 리턴)→최종처리메소드

 

람다식의 실행문은 여러줄이 될 수 있음. 필요하다면 공백을 제거하기 위해 trim(), 형변환을 위해 parseInt()사용

 

 

 

flatMapXXX() 메소드의 종류

더보기
리턴타입 메소드(매개변수) 요소->대체요소
Stream<R> flatMap(Function<T, Stream<R>>) T -> Stream(R)
DoubleStream flatMap(DoubleFunction<DoubleStream>) double -> DoubleStream
IntStream flatMap(IntFunction<IntStream>) int -> IntStream
LongStream flatMap(LongFunction<LongStream>) long -> LongStream
DoubleStream flatMapToDouble(Function<T, DoubleStream>) T -> DoulbeStream
IntStream flatMapToInt(Function,<T, IntStream>) T -> IntStream
LongStream flatMapToLong(Function<T, LongStream>) T -> LongStream

 

 


mapXXX()메소드

요소를 대체하는 복수 개의 요소들로 구성된 새로운 스트림을 리턴한다

원래 스트림 : B A → A는 C로, B는 D로 → 새로운 스트림  D C

 

studentList.stream().mapToInt(Student::getScore).forEach(System.out:println);

오리지널 스트림 → 객체를 int로 매핑 → 최종처리

 

mapXXX() 메소드의 종류 p.802

 


 

asDoubleStream(), asLongStream(), boxed() 메소드

 

asDoubleStream() : 요소를 타입변환해서 새로운 스트림 생성 (IntStream 혹은 LongStream →DoubleStream)

asLongStream() : 요소를 타입변환해서 새로운 스트림 생성 (IntStream →LongStream)

boxed() : int, long, double 요소를 Integer, Long, Double 요소로 박싱해서 스트림 생성

 

 

Arrays.stream(intArray).asDoubleStream().forEach(System.out:println);

오리지널 스트림 생성 → 객체의 요소 변환 매핑 → 최종처리

Arrays.stream(intArray).boxed().forEach(obj->System.out.println(obj.intValue()));

 

오리지널 스트림 생성 → Integer로 박싱 → 최종처리(forEach에서 객체가 매개변수면 박싱 여부를 알아보기 위해 객체.intValue() 호출)


예제

더보기
	public static void main(String[] args) {
		List<String> inputList1 = Arrays.asList("java8 lambda", "stream mapping");
		//스트림 -> flatMap :요소들로 새로운 스트림 만들기 (data를 " "를 구분자로 쪼개서 다시 stream)
		// Arrays.stream 스트림 만듦(data.split(" ") 배열을 두개의 요소로 쪼갬)
		inputList1.stream().flatMap(data->Arrays.stream(data.split(" "))).forEach(word->System.out.println(word));
		System.out.println();
		
		List<String> inputList2 = Arrays.asList("10, 20, 30", "40, 50, 60");
		inputList2.stream().flatMapToInt(data->{
			String[] strArr = data.split(","); //,로 쪼개어 strArr에 저장
			int[] intArr = new int[strArr.length]; //int 배열 만들기
			for(int i=0; i<strArr.length; i++) { //0열부터 trim()으로 숫자 앞 공백을 없애고 int로 형변환하여 intArr에 넣기 
				intArr[i] = Integer.parseInt(strArr[i].trim());
			}
			return Arrays.stream(intArr); //다시 스트림으로 만들어 리턴
		}).forEach(number->System.out.println(number));
	}
		List<Student> studentList  = Arrays.asList(
			new Student("홍길동", 10),
			new Student("신용권", 20),
			new Student("유미선", 30)
			);
		studentList.stream().mapToInt(Student::getScore).forEach(score->System.out.println(score));
	}
}
	public static void main(String[] args) {
		int[] intArray = {1,2,3,4,5};
		
		IntStream intStream = Arrays.stream(intArray);
		intStream.asDoubleStream().forEach(System.out::println);
		System.out.println();
		
		intStream = Arrays.stream(intArray);
		intStream.boxed().forEach(obj-> System.out.println(obj.intValue()));
	}

 

 

 


정렬 [ Sorted() ]

 

1. 숫자 요소 정렬 

sorted() : 오름차순 정렬 → 리턴타입 : DoubleStream, IntStream, LongStream

intStream.sorted().forEach(n->System.out.print(n)


 

2. 객체 요소 정렬 (Comparable을 구현할 때)

Comparable 구현 방법에 따라 정렬, Comparable을 구현한 요소에서만 sorted() 메소드 호출 가능

Comparable을 구현한 상태에서 오름차순 정렬 방법

① sorted()

② sorted( (a,b) -> a.compareTo(b) )

③ sorted( (Comparator.naturalOrder) )

 

Comparable을 구현한 상태에서 내림차순 구현방법

① sorted( (a,b) -> b.compareTo(a) )

② sorted( Comparator.reverseOrder())

 


3. 객체 요소 정렬(Comparator의 람다식 표현)

sorted(Comparator<T>) - Comparator에 따라 정렬

Comparable을 구현하지 않았다면, Comparator를 매개값으로 갖는 sorted()를 이용한다

Comparator를 구현하는 방법은 1. 익명클래스 2.람다식 → 보기는 람다식을 이용해 구현

sorted( (a,b) -> { ...a와 b 비교 작으면 음수, 같으면 0, 크면 양수 리턴하는 코드 } )

sorted( (a,b) -> a.getScore()-b.getScore())

 

 

더보기
public static void main(String[] args) {
	List<Student> studentList = Arrays.asList(new Student("홍길동", 30), new Student("신용권",10), new Student("유미선", 20));
	//sorted() 하면 알아서 compareTo기준으로 정렬됨
	studentList.stream().sorted().forEach(s->System.out.println(s.getName()+ " " + s.getScore()));
	System.out.println("=============");
	studentList.stream().sorted(Comparator.reverseOrder()).forEach(s->System.out.println(s.getName()+ " " + s.getScore()));
	
	}
}
class Student implements Comparable<Student>{
	private String name;
	private int score;
	@Override
	public int compareTo(Student o) {
		return name.compareTo(o.name); //이름 오름차순
//		return o.name.compareTo(name); //이름 내림차순
//		return Integer.compare(score, o.score); //점수순
	}
	Student(String name, int score){
		this.name = name;
		this.score = score;
	}
	String getName(){ return name; }
	int getScore(){ return score; }
}
public class SortingExample {
	public static void main(String[] args) {
		//숫자 요소일 경우
		IntStream intStream = Arrays.stream(new int[] {5,3,2,1,4});
		intStream.sorted().forEach(n->System.out.print(n + ","));
		System.out.println();
		
		//객체 요소일 경우
		List<Student1> studentList = Arrays.asList(
			new Student1("홍길동",30), new Student1("신용권",10), new Student1("유미선", 20));
		
		studentList.stream().sorted().forEach(s->System.out.print(s.getScore() + ","));
		System.out.println();
		studentList.stream().sorted(Comparator.reverseOrder()).forEach(s->System.out.print(s.getScore() + ","));
	}
}

class Student1 implements Comparable<Student1> {
	private String name;
	private int score;
	Student1(String name, int score) {this.name = name;	this.score = score;}
	@Override
	public int compareTo(Student1 o) {return Integer.compare(score, o.score);}
	String getName(){return name;}
	int getScore() {return score;}
}
public class SortingExample2 {
	public static void main(String[] args) {
		//객체 요소일 경우
		List<Student2> studentList = Arrays.asList(
			new Student2("홍길동",30), new Student2("신용권",10), new Student2("유미선", 20));
		
		//오름차순 정렬
		studentList.stream().sorted((a,b)-> a.getScore()-b.getScore()).forEach(s->System.out.print(s.getScore()+","));
		
		//내림차순 정렬
		System.out.println();
		studentList.stream().sorted((a,b)->b.getScore()-a.getScore()).forEach(s->System.out.print(s.getScore()+","));
	}
}

class Student2 {
	private String name;
	private int score;
	int getScore() { return score;}
	Student2(String name, int score) {this.name = name;	this.score = score;}
}

 


루핑[ peek(), forEach() ]

루핑은 요소 전체를 반복하는 것으로, peek(), forEach()가 있다

 

peek()

중간처리 메소드로, 반드시 최종처리 메소드가 호출되어야 동작한다

즉, 최종처리 메소드를 작성하지 않고 peek()로 끝나는 파이프라인은 동작하지 않는다

주로 중간 스트림의 진행 상황 알고 싶을 때 peek(출력문)을 이용한다

 

forEach()

최종처리 메소드로 파이프라인 마지막에 루핑하면서 요소를 하나씩 처리한다

sum()과 같은 다른 최종 메소드와 함께 호출 불가능하다

 

 

더보기
import java.io.OptionalDataException;
import java.util.Arrays;
import java.util.OptionalDouble;
import java.util.OptionalInt;

public class LoopingEx {
//809
	public static void main(String[] args) {
		int[] intArray = {1,2,3,4,5};
		
		System.out.println("peek()를 마지막에 호출한 경우");
		//최종스트림 sum이 있어야 출력가능
		int total = Arrays.stream(intArray).filter(a->a%2 ==0).peek(n->System.out.println(n)).sum();
		System.out.println(total+"=======");
		//peek() 중간 스트림이 어떻게 되는지 알아볼 수 있음. peek(출력) 
		total = Arrays.stream(intArray).peek(n->System.out.print(n + " ")).filter(a->a%2 ==0).peek(n->System.out.println(n)).sum();
		System.out.println(total);
												//filter로 넘어온 값이 이 조건과 맞냐? false
		boolean result = Arrays.stream(intArray).filter(a->a%2==1).allMatch(a->a%2==0);
		System.out.println(result);
		result = Arrays.stream(intArray).anyMatch(a->a%2==0);
		System.out.println(result);
		total = Arrays.stream(intArray).sum();
		OptionalDouble avg = Arrays.stream(intArray).average();
		//클래스로 나온 평균을 기본형인 Double로 고치려면
		double davg = avg.getAsDouble();
		System.out.println(davg);
		
		int maxValue = Arrays.stream(intArray).max().getAsInt();
		System.out.println("최대값 : " + maxValue);
		
		int minValue = Arrays.stream(intArray).min().getAsInt();
		System.out.println("최소값 : " + minValue);
		
		//findFirst()붙이면 최종스트림이라 오류는 안남. 단, int값으로 알고싶다면 getAsInt()
		OptionalInt op = Arrays.stream(new int[] {1,2,3,4,5,6}).filter(n->n%3==0).findFirst();
		System.out.println(op.getAsInt());
	}
}

매칭[ allMatch(), anyMatch(), noneMatch() ]

요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지 조사

 

allMatch()

모든 요소들이 만족하는지

 

anyMatch()

하나라도 만족하는지

 

noneMatch()

모두 만족하지 않는지

 

더보기
public static void main(String[] args) {
	int[] intArr = { 2, 4, 6};
	boolean result = Arrays.stream(intArr).allMatch(a->a%2==0);
	System.out.println("모두 2의 배수인가? " + result);
		
	result = Arrays.stream(intArr).anyMatch(a->a%3==0);
	System.out.println("하나라도 3의 배수가 있는가? " + result);
		
	result = Arrays.stream(intArr).noneMatch(a->a%3==0);
	System.out.println("3의 배수가 없는가? " + result);
}

 


기본 집계[ sum(), count(), average(), max(), min(), findFirst() ]

집계(Aggregate)는 최종처리기능으로, 대량의 데이터를 가공해서 축소하는 리덕션

 

리턴되는 OptionalXXX는 값을 저장하는 값 기반 클래스(value-based class)이다.

실제로 값을 얻기 위해서는 get(), getAsDouble(), getAsInt(), getAsLong()을 호출하면 된다.

cf. findFirst() 첫번째 요소

 

Optional 클래스

옵셔널 클래스는 단순 집계 값 뿐만 아니라 집계 값이 존재하지 않을 경우 디폴트 값을 설정하거나, 집계 값을 처리하는 Consumer도 등록할 수 있다.

 

NoSuchElementException (요소가 없는데 집계처리할 때 예외) 방지하는 방법

1. optional.isPresent()

     if(optional.isPresent()) { ... } else { ... }

2. orElse(디폴트값)

     최종처리 메소드 끝에 .orElse(디폴트값)

3.ifPresent()

    최종처리 메소드 끝에 .ifPresent(실행문)

 

더보기
	public static void main(String[] args) {
//		int[] arr = {1,2,3,4,5};
//		List<Integer> list2 = Arrays.asList(arr);
		List<Integer> list = new ArrayList<>();
/*		예외발생
		double avg = list.stream().mapToInt(Integer::intValue).average().getAsDouble();
*/		
		//방법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));
	}

커스텀 집계[ reduce() ]

 

 

 

인터페이스 리턴타입 메소드(매개변수)
Stream Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
IntStream OptionalInt reduce(IntBinaryOperator op)
int reduce(int identity, IntBinaryOperator op)
LongStram OptionalLong reduce(LongBinaryOperator op)
long reduce(long identity, LongBinaryOperator op)
DoubleStream OptionalDouble reduce(DoubleBinaryOperator op)
double reduce(double identity, DoubleBinaryOperator op)
첫번째 매개변수에 identity(디폴트값)을 준다면 기본형으로 리턴되고, 그렇지 않다면 Optional형으로 리턴
디폴트값을 줌으로써 스트림에 요소가 없어도 예외 발생X

 

더보기

public class ReductionExample {
	public static void main(String[] args) {
		List<Student> studentList = Arrays.asList(
			new Student("홍길동", 92), 
			new Student("신용권", 95), 
			new Student("김자바", 88));
		
		int sum1 = studentList.stream().mapToInt(Student::getScore).sum();
		System.out.println("sum() 메소드로" + sum1);
		
		//BinaryOperator<T>의 추상 메소드 R apply(T t, U u) - 중간에 어떤 요소가 넘어가는지 알고싶어 peek
//		sum1 = studentList.stream().map(Student::getScore).peek(s->System.out.println(s)).reduce((t,u) -> t+u).get();
		sum1 = studentList.stream().map(Student::getScore).reduce((t,u) -> t+u).get();
		System.out.println("reduce() 메소드로 " + sum1);
		
		int sum2 = studentList.stream().map(Student::getScore).reduce(0, (a,b)->a+b); //초기값 0. 리턴타입이 Integer이라 바로 int에 담을 수 있음
		System.out.println("reduce() 메소드로 " + sum2);
		
		int sum3 = studentList.stream().map(Student::getScore).reduce(
				(t,u)->{if(t>u) return t;else return u;}).get();
		System.out.println("reduce()로 최대값: " + sum3);
	}
}

 

 


수집[ collect() ]

처리 결과를 기본형으로 출력하지 않고 다시 컬렉션으로 만들어 내는 것

 

Stream의 collect(  Collector<T,A,R>  collector)

 

Collector (수집기) : 어떤 요소를 어떤 컬렉션에 수집할 것인지를 결정

타입파라미터 T는 요소, A는 누적기(accumulator), R은 요소가 저장될 컬렉션 즉, T 요소를 A 누적기가 R에 저장

 

Collector의 구현 객체는 Collectors 클래스의 정적 메소드를 통해 얻을 수 있다

리턴타입 Collectors의 정적 메소드 설명
Collector<T,?,List<T>> toList() T를 List에 저장
Collector<T,?,Set<T>> toSet() T를 Set에 저장
Collector<T,?,Collection<T>> toCollection( Supplier<Collection<T>> ) T를 Supplier가 제공한 Collection에 저장
Collector<T,?,Map<K,U>> toMap(
Funtion<T,K> keyMapper,
Function<T,U> valueMapper)
T를 K와 U로 매핑해서 K를 키로, U를 값으로 Map에 저장
Collector<T,?, ConcurrentMap<K,U>> toConcurrentMap(
Funtion<T,K> keyMapper,
Funtion<T,U> valueMapper)
T를 K와 U로 매핑해서 K를 키로, U를 값으로 ConcurrentMap에 저장
A 누적기가 ?로 되어 있는 건, Collector가 R에 T를 저장하는 방법을 알고 있어 A누적기가 필요없기 때문이다.
Map은 스레드에 안전하지 않고, ConcurrentMap은 안전하다(멀티 스레드 환경에서 권장)

 

 

여학생만 필터링해서 별도의 HashSet 생성

 

더보기

① 전체 학생 스트림 만들기 [자료.stream()]

      Stream<Student> totalStream = totalList.stream();

 

② 여학생 필터링하기 [filter(조건 람다식)]

      Stream<Student> femaleStream = totalStream.filter(s->s.getSex() == Student.Sex.FEMALE);

 

③ Supplier<HashSet<Student>> 만들기

     Supplier 인터페이스 타입은 인자를 받지 않고 리턴만 해주는 타입으로, 즉 input없이 무언가를 리턴해주는 식이 Supplier자리에 와야한다

     여기서 무언가는 HashSet으로,  <Student>를 파라미터타입으로 가지는 HashSet을 생성하는 Supplier의 익명 구현객체 (내지 람다식)이 필요하다.

     Supplier<HashSet<Student>> supplier = HashSet :: new;

cf.HashSet :: new는 () -> new HashSet()와 같다

④ 3의 supplier를 매개값으로 Collector를 구현 (Collectors 클래스의 정적 메소드 toCollection)

     Collector<Student, ? , HashSet<Student>> collector = Collectors.toCollection(supplier);

 

⑤ 4의 collector을 매개값으로 가지는 collect() 를 통해 새로운 컬렉션 담기

     Set<Student> femaleSet = femaleStream.collect(collector);           //1~2와 3~4를 연결

 

 요약 

Collectors.toCollection(HashSet::new)

Collectors.toSet()

 studentList.stream().filter(s->s.getSex()==Student.Sex.FEMALE).collect(Collectors.toCollection(HashSet::new)); 

studentList.stream().filter(s->s.getSex()==Student.Sex.FEMALE).collect(Collectors.toSet());  //toSet으로 더욱 간단하게 생성 가능

 

남학생 필터링해서 별도의 List만들기

Collectors.toList()

Collectors.toCollection(ArrayList::new)

studentList.stream().filter(s->s.getSex()==Student.Sex.MALE).collect(Collectors.toList());

studentList.stream().distinct().filter(s->s.getSex()==Student.Sex.MALE).collect(Collectors.toCollection(ArrayList::new));

 

서울 사는 학생의 자료를 HashMap으로 만들기

Collectors.toMap(keyMapper,valueMapper)

HashMap sM = studentList.stream().filter(s->s.getCity()==Student.City.Seoul).collect(Collectors.toMap(s->s.getName()s->s.getCity()));

 

좀 외우라고 넣어두는 코드 (정답 왼쪽하단)

더보기

클래스 안에 다음과 같이 열거값을 표현하면, 꺼내올때도 Student.City.Busan으로 사용 가능

enum Sex{MALE,FEMALE};
enum City{Seoul, Busan};

	public static void main(String[] args){
		
	//소스만들기
	List<Student> studentList = Arrays.asList(
			new Student("홍길동", 10, Student.Sex.MALE),new Student("김수애", 6, Student.Sex.FEMALE),
			new Student("신용권", 10, Student.Sex.MALE),new Student("박수미", 6, Student.Sex.FEMALE),
			new Student("신용권", 10, Student.Sex.MALE),new Student("박수미", 6, Student.Sex.FEMALE));
			//주소로 비교해서 같아도 같은줄 모름
	//남학생만 묶어서 List 생성
	List<Student> maleList = studentList.stream().distinct().filter(s->s.getSex()==Student.Sex.MALE).collect(Collectors.toList());
   	List<Student> maleList2 = studentList.stream().distinct().filter(s->s.getSex()==Student.Sex.MALE).collect(Collectors.toCollection(ArrayList::new));

	maleList.stream().map(s->s.getName()).forEach(System.out::print);
	
	System.out.println();
	
	//여학생들만 묶어서 List 생성
	Set<Student> femaleSet = studentList.stream().filter(s->s.getSex()==Student.Sex.FEMALE).collect(Collectors.toCollection(HashSet::new));
	Set<Student> femaleSet2 = studentList.stream().distinct().filter(s->s.getSex()==Student.Sex.FEMALE).collect(Collectors.toSet());
	
	Iterator<Student> it = femaleSet.iterator();
	while(it.hasNext()) {Student s = it.next(); System.out.print(s.getName() + " " +s.getScore() + " "  + s.getSex()+"\n");}
	
	femaleSet2.stream().forEach(s->System.out.println(s.getName() +" " + s.getScore() + " " + s.getSex() + " " + s.getCity()));
    
    //서울 사는 사람만  Map생성하기 (키 - 이름, 값 - 도시명)
	HashMap sM =
	(HashMap) studentList.stream().filter(s->s.getCity()==Student.City.Seoul).collect(Collectors.toMap(s->s.getName(), s->s.getCity()));
	System.out.println(sM.get("홍길동"));
	System.out.println(sM.get("신용권"));
	System.out.println(sM.get("김수애"));
	
	}
}

//822
class Student implements Comparable<Student>{
	enum Sex{MALE,FEMALE};
	enum City{Seoul, Busan};
	
	private String name;
	private int score;
	private Sex sex;
	private City city;
	
	@Override //정렬 기준 처리
	public int compareTo(Student o){
		return name.compareTo(o.name);
	}
	
	@Override //동등객체 처리
	public boolean equals(Object obj){
		if(obj instanceof Student) {
			Student s = (Student) obj;
			return name.equals(s.name);
		}
		return false;
	}
	
	@Override
	public int hashCode(){
		return name.hashCode();
	}
	Student(String name, int score, Sex sex){ this.name = name; this.score = score; this.sex = sex;}
	Student(String name, int scroe, Sex sex, City city) {this.name = name; this.score = score; this.sex = sex; this.city = city;}
	Sex getSex(){ return sex; }
	City getCity(){ return city; }
	String getName(){ return name; }
	int getScore(){ return score; }
}

 

 


 

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

 

collect( Supplier<R>, XXXConsumer<R>, BiConsumer<R,R> )

Supplier<R> 요소들이 수집될 컨테이너 객체(R)을 생성

순차처리에선 한번, 병렬처리 스트림에선 여러번 실행했다가 하나로 결합됨

 

XXXConsumer<R> 

컨테이너 객체(R)에 요소(T)를 수집

요소를 수집할 때마다(요소 수만큼) XXXConsumer이 실행됨

 

BiConsumer<P,R>

컨테이너 객체(R)을 결합

순차처리에선 호출되지 않지만 여전히 필요

병렬처리에선 호출되어 최종 컨테이너 객체를 완성

 

 

스트림에서 읽은 남학생을 MaleStudent에 수집하는 코드

더보기

자세한 버전

① Stream<Student> totalStream = totalList.stream();

② Stream<Student> maleStream = totalStream.filter(s->s.getSex()==Student.Sex.MALE);

 

③ Supplier<MaleStudent> supplier = () -> new MaleStudent();

      저장할 객체 만드는 Supplier

④ BiConsumer<MaleStudent, Student> accumulator = (ms, s) -> ms.accumulate(s);

      자료 수집할 객체 ms에 accumulate 메소드로 요소의 student 객체 저장

⑤ BiConsumer<MaleStudent, MaleStudent> combiner = (ms1, ms2) -> ms1.combine(ms2);

     3에서 생성된 객체가 여럿이라면 각 객체에 저장된 student 객체를 하나의 객체에 결합하여 수집

⑥ MaleStudent maleStudent = maleStream.collect.collect(supplier, accumulator, combiner);

     1~2와 3~5의 과정을 하나로 만듦

 

 

 요약 

MaleStudent maleStudent = totalList.stream().filter(s->s.getSex() == Student.Sex.Male)

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

                   //accumulate이나 combine에서 매개변수가 두 개임에도 메소드 호출이 가능한 이유는,

메소드 선언 시 두 매개변수의 타입이 달라 자동 추정 가능

 

 

 

예시

더보기
//826page에 main 메소드 있음 
public class MaleStudentEx {
   public static void main(String[] args) {
      //소스만들기 
      List<Student> studentList = Arrays.asList( 
            new  Student("홍길동", 10, Student.Sex.MALE, Student.City.Seoul), 
            new  Student("김수애", 6, Student.Sex.FEMALE, Student.City.Seoul), 
            new  Student("신용권", 10, Student.Sex.MALE, Student.City.Pusan), 
            new  Student("박수미", 6, Student.Sex.FEMALE, Student.City.Pusan)
         );
      
      //남학생만 수집하되 사용자 정의한 컨테이너에 저장하세요
      /*MaleStudent maleStudent = studentList.stream().filter(s->s.getSex() == Student.Sex.MALE)
                                                    .collect(MaleStudent::new,MaleStudent::accumlate, MaleStudent::combine);*/
   
      //오리지널 스트림에서 병렬처리하기 위해서 parallelStrem()
      MaleStudent maleStudent = studentList.parallelStream().filter(s->s.getSex() == Student.Sex.MALE)
                .collect(MaleStudent::new,MaleStudent::accumlate, MaleStudent::combine);
      
      
      List<Student> mSL = maleStudent.getList();
        mSL.stream().forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
   }
}

//남학생이 저장되는 컨테이너 
class MaleStudent{
   
   private List<Student> list;
   
   public MaleStudent() {
      list = new ArrayList<Student>();
      System.out.println("[" + Thread.currentThread().getName() + "] MaleStudent ()");
   }
   
   public void accumlate(Student student) {
      list.add(student);
      System.out.println("[" + Thread.currentThread().getName() + "] accumlate ()");
   }
   
   public void combine(MaleStudent other) {
      list.addAll(other.getList());// Collection에 있는 요소를 모두 리스트에 추가해라 
      System.out.println("[" + Thread.currentThread().getName() + "] combine ()");
   }
   public List<Student> getList(){
      return list;
   }
}

//Student 클래스 
class Student  implements Comparable<Student> {
    public enum Sex {MALE, FEMALE};
    public enum City { Seoul, Pusan };
    
     private String name;
     private int score;
     private Sex sex;
     private City city;
     
   //정렬 기준 처리   
     @Override
   public int compareTo(Student o) { return name.compareTo(o.name); }
     
   //동등객체 처리  
     @Override
   public int hashCode() { return name.hashCode(); }
     
     @Override
   public boolean equals(Object obj) {
      if(obj instanceof Student) {
         Student s = (Student) obj;
         return name.equals(s.name);
      }
      return false;
   }
     
   public Student(String name, int score, Sex sex) {
        this.name = name;
        this.score = score;
        this.sex = sex;
      }
     public Student(String name, int score, Sex sex, City city) {
        this.name = name;
        this.score = score;
        this.sex = sex;
        this.city = city;
      }
   public Sex getSex() { return sex; }
   public City getCity() { return city; }
   public String getName() { return name; }
   public int getScore() { return score; }
}

 

 


요소를 그루핑해서 수집 [ groupingBy() ]

collect()는 단순 요소를 수집하는 것 외에도 컬렉션의 요소들을 그룹핑해서 Map 객체를 생성한다.

collect()를 호출할 때 Collectors의 groupingBy() 또는 groupingByConcurrent()가 리턴하는 Collector를 매개값으로 대입하면 된다.

groupingByConcurrent()는 스레드에 안전한 ConcurrentMap을 생성한다.

 

 

Collector<T,?,Map<K,List<T>>> collector = Collectors.groupingBy (Function<T,K> classifier)T를 K로 매핑하고 K키에 저장된 List에 T를 저장한 Map 생성즉, T→K를 매핑한 것을 Key로 하고, T객체의 리스트를 Value로 가지는 Map을 생성

 

 

Collector<T,?,Map<K,D>> collector = Collectors.groupingBy (Funtion<T,K> classifier, Collector<T,A,D> collector)T를 K로 매핑하고 K키에 저장된 D객체에 T를 누적한 Map 생성

 

 

Collector<T,?,Map<K,D>> = Collectors.groupingBy (Funtion<T,K> classifier, Supplier<Map<K,D>> mapFactory, Collector<T,A,D> collector)

T를 K로 매핑하고 Supplier가 제공하는 Map에서 K키에 저장된 D객체에 T를 누적

 

 

학생을 성별로 그룹핑 후 그룹별 학생 List 생성, 성별을 키로 학생 List를 값으로 가지는 Map생성

더보기

① Stream<Student> totalStream = totalList.stream();

② Function<Student, Student.Sex> classfier = Student :: getSex;

③ Collector<Student, ?, Map<Student.Sex, List<Student>>>> collector =  Collectors.groupingBy (classifier);

     Student 요소를 Map에 수집하도록 하는 Collector, Map의 키는 성별, 값은 Student객체

④ Map<Student.Sex, List<Student>> mapBySex = totalStream.collect(collector);  

 

 요약 

Map<Student.Sex, List<Student>> mapBySex = totalList.stream().collect(Collectors.groupingBy(Student::getSex));

 

 

 

 

거주 도시별 그룹핑 후 그룹별 학생들의 이름 List 생성, 거주 도시를 키로 이름 List를 값으로 가지는 Map생성

더보기

① Stream<Student> totalStream = totalList.stream();

② Function<Student, Student.City> classifier  = Student ::getCity;

③ Function<Student, String> mapper = Student::getName;

④ Collector<String, ?, List<String>> collector1 = Collectors.toList();

     수집할 요소는 이름, 수집할 1번 객체는 이름 List

⑤ Collector<Student, ?, List<String>>  collector2  = Collectors.mapping(mapper, collector1);

      수집할 요소는 Student 객체, 수집할 2번 객체는 학생의 이름을 이름 List로 바꾼 것 

⑥ Collector<Student, ?, Map<Student.City, List<String>>> collector3 = Collectors.groupingBy(classifier, collector2);

      수집할 요소는 Student객체, 저장 객체는 Map으로 Student.City가 키, 학생의 이름 List가 값인 Map

⑦ Map<Student.City, List<String>> mapByCity = totalStream.collect(collector3);

 

 요약 

Map<Student.City, List<String>> mapByCity = totalList.stream().

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

 

 

 

예시

더보기
   public static void main(String[] args) {
      //소스만들기 
            List<Student> totalList = Arrays.asList( 
                  new  Student("홍길동", 10, Student.Sex.MALE, Student.City.Seoul), 
                  new  Student("김수애", 6, Student.Sex.FEMALE, Student.City.Seoul), 
                  new  Student("신용권", 10, Student.Sex.MALE, Student.City.Pusan), 
                  new  Student("박수미", 6, Student.Sex.FEMALE, Student.City.Pusan)
               );
           //성별로 그룹핑함
               Map<Student.Sex, List<Student>>  mapBySex = totalList.stream().collect(Collectors.groupingBy(Student::getSex));
            //남학생만 출력
               System.out.println("남학생");
               mapBySex.get(Student.Sex.MALE)
               .stream()
               .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
               //여학생만 출력
               System.out.println("여학생");
               mapBySex.get(Student.Sex.FEMALE)
               .stream()
               .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
               
           //도시별로 그룹핑하기
               Map<Student.City,  List<Student>>  mapByCity =totalList.stream().collect(Collectors.groupingBy(Student::getCity));
         //서울만 출력
               System.out.println("서울학생");
             mapByCity.get(Student.City.Seoul)
               .stream()
               .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
         //부산만 출력
               System.out.println("부산학생");
             mapByCity.get(Student.City.Pusan)
               .stream()
               .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
               
         //점수별로 그룹핑 하기 
                Map<Integer,  List<Student>>  mapByScore =totalList.stream().collect(Collectors.groupingBy(Student::getScore));
            //10점 만 출력
                  System.out.println("10점인 학생");
                  mapByScore.get(10)
                  .stream()
                  .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
            //6점만 출력
                  System.out.println("6점인 학생");
                  mapByScore.get(6)
                  .stream()
                  .forEach(s->  System.out.println( s.getName() + " " + s.getScore()  + "  " + s.getSex() + " " + s.getCity()));
   }
}

//Student 클래스 
class Student  implements Comparable<Student> {
    public enum City { Seoul, Pusan };
    
     private String name;
     private int score;
     private Sex sex;
     private City city;
     
   //정렬 기준 처리   
     @Override
   public int compareTo(Student o) { return name.compareTo(o.name); }
     
   //동등객체 처리  
     @Override
   public int hashCode() { return name.hashCode(); }
     
     @Override
   public boolean equals(Object obj) {
      if(obj instanceof Student) {
         Student s = (Student) obj;
         return name.equals(s.name);
      }
      return false;
   }
     
   public Student(String name, int score, Sex sex) {
        this.name = name; this.score = score; this.sex = sex;}
     public Student(String name, int score, Sex sex, City city) {
        this.name = name; this.score = score; this.sex = sex; this.city = city; }
   public Sex getSex() { return sex;}
   public City getCity() { return city; }
   public String getName() { return name; }
   public int getScore() { return score;}
}

 

 


그룹핑 후 매핑 및 집계

Collectors.groupingBy()는 그룹핑 후 매핑이나 집계(평균, 카운팅, 연결, 최대, 최소, 합계)를 할 수 있도록 두번째 매개값으로 Collector를 가질 수 있다.

 

 

 

Collecotr를 리턴하는 Collectors의 메소드

mapping(Function<T,U> mapper, Collector<U,A,R> collector) T를 U로 매핑한 후, U를 R에 수집
averagingDouble(ToDoubleFunction<T> mapper) T를 Double로 매핑한 후, Double의 평균값을 산출
counting() T의 카운팅 수를 산출
joining(CharSequence delimiter) CharSequence를 구분자(delimiter)로 연결한 String을 산출
maxBy(Comparator<T> comparator) Comparator를 이용해서 최대 T를 산출
minBy(Comparator<T> comparator) Comparator를 이용해서 최소 T를 산출
summingInt(ToIntFunction)
summingLong(ToLongFunction)
summingDouble(ToDoubleFunction)

 Int, Long, Double 타입의 합계 산출

 

성별로 그룹핑 후 그룹별 평균 점수를 구하고, 성별을 키로 평균점수를 값으로 갖는 Map생성

Map<Student.Sex, Double> mapBySex = totalList.stream()

.collect(Collectors.groupingBy(Student::getSex, Collectors.averagingDouble(Student :: getScore)));

 

성별로 그룹핑한 다음 그룹별 학생 이름을 쉼표로 구분해서 문자열로 만들고, 성별을 키 이름 문자열을 값으로 갖는 Map생성

Map<Student.Sex, String> mapByName = totalList.stream()

.collect(Collectors.groupingBy(Student::getSex, Collectors.mapping(Student::getName, Collectors.joining(","))));

 

 

 

예시

더보기
	public static void main(String[] args) {
		
		List<Student> studentList = Arrays.asList(
		new Student("홍길동", 10, Student.Sex.MALE, Student.City.Seoul),
		new Student("김수애", 6, Student.Sex.FEMALE, Student.City.Busan),
		new Student("신용권", 10, Student.Sex.MALE, Student.City.Seoul),
		new Student("박수미", 6, Student.Sex.FEMALE, Student.City.Seoul));
		
		//성별로 평균 점수를 저장하는 Map<성별, 평균점수> 얻기
		Map<Student.Sex, Double> mapBySex = studentList.stream().
				collect(Collectors.groupingBy(Student::getSex, Collectors.averagingDouble(Student::getScore)));
		System.out.println("남학생 평균 점수: ");
		double maleAverage = mapBySex.get(Student.Sex.MALE);
		System.out.println(maleAverage);
		System.out.println("여학생 평균 점수: ");
		double femaleAverage = mapBySex.get(Student.Sex.FEMALE);
		System.out.println(femaleAverage);
		
		//남학생의 점수합계
		Map<Student.Sex, Integer> mapBySex2 = studentList.stream().
				collect(Collectors.groupingBy(Student::getSex, Collectors.summingInt(Student::getScore)));
		System.out.println("남학생의 점수 합계: ");
		int maleSum = mapBySex2.get(Student.Sex.MALE);
		System.out.println(maleSum);
		
		//부산사람의 최고점
		//1단계 비교하는 Comparator 구현
		Comparator<Student> comparator = new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				return o1.getScore() - o2.getScore();
			}
		};
		//2단계 스트림으로 Map을 만든다
		Map<Student.City, Optional<Student>> busanByScore = studentList.stream()
				.collect(Collectors.groupingBy(Student::getCity, Collectors.maxBy(comparator)));
		//3단계 value에 해당하는 것을 Optional로 받는다
		Optional<Student> busanMax = busanByScore.get(Student.City.Busan); //부산의 최대 점수가 있는 Student의 주소 출력
		//4단계 Optional로 된 것을 Student 클래스로 만든다. 부산의 최대 점수가 있는 Student의 주소가 나온다
		Student s = busanMax.get();
		//5단계 s를 이용해서 자료를 출력한다
		System.out.println(s.getName()+ " " + s.getScore() + " " + s.getCity());
		
	}
}
class Student implements Comparable<Student>{
	enum Sex{MALE,FEMALE};
	enum City{Seoul, Busan};
	
	private String name;
	private int score;
	private Sex sex;
	private City city;
	
	@Override //정렬 기준 처리
	public int compareTo(Student o) {
		return name.compareTo(o.name);
	}
	
	@Override //동등객체 처리
	public boolean equals(Object obj) {
		if(obj instanceof Student) {
			Student s = (Student) obj;
			return name.equals(s.name);
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return name.hashCode();
	}
	Student(String name, int score, Sex sex){ this.name = name; this.score = score; this.sex = sex;}
	Student(String name, int scroe, Sex sex, City city) {this.name = name; this.score = score; this.sex = sex; this.city = city;}
	Sex getSex(){ return sex; }
	City getCity() { return city; }
	String getName() { return name; }
	int getScore() { return score; }
}
public class GroupingAndReductionExample {
	public static void main(String[] args) {
		List<Student> totalList = Arrays.asList(
				new Student("홍길동", 10, Student.Sex.MALE),
				new Student("김수애", 12, Student.Sex.FEMALE),
				new Student("신용권", 10, Student.Sex.MALE),
				new Student("박수미", 12, Student.Sex.FEMALE));
		
		//성별로 평균 점수를 저장하는 Map 얻기
		Map<Student.Sex, Double> mapBySex = totalList.stream().
				collect(Collectors.groupingBy(Student::getSex, Collectors.averagingDouble(Student::getScore)));
		System.out.println("남학생 평균 점수: " +mapBySex.get(Student.Sex.MALE));
		System.out.println("여학생 평균 점수: " +mapBySex.get(Student.Sex.FEMALE));
		
		//성별을 쉼표로 구분한 이름을 저장하는 Map 얻기
		Map<Student.Sex, String> mapByName = totalList.stream().
				collect(Collectors.groupingBy(Student::getSex, Collectors.mapping(Student::getName, Collectors.joining(","))));
		System.out.println("남학생 전체 이름 : " + mapByName.get(Student.Sex.MALE));
		System.out.println("여학생 전체 이름 : " + mapByName.get(Student.Sex.FEMALE));
	}
}

 

 


병렬처리

 

 

확인문제

더보기

Q5.

		List<String> list = Arrays.asList(
				"This is a java book",
				"Lambda Expressions",
				"Java8 supports lambda expressions"
				);
		list.stream().filter(a->a.contains("java")|| a.contains("Java")).forEach(a->System.out.println(a));
		list.stream().filter(a->a.toLowerCase().contains("java")).forEach(System.out::println);

 

 

Q6.

public class StreamExample2 {
	public static void main(String[] args) {
		List<Member> list = Arrays.asList(
			new Member("홍길동", 30),
			new Member("신용권", 40),
			new Member("김자바", 26));
		
		double avg = list.stream().mapToInt(s->s.getAge()).average().getAsDouble();
		System.out.println(avg);
    }
static class Member{
	private String job;
	private String name;
	private int age;
	Member(String name, String job){ this.name = name; this.job = job;}
	Member(String name, int age){
		this.name = name;
		this.age = age;
	}
	String getName() { return name; }
	int getAge() { return age; }
	String getJob() { return job; }
	}
}

 

 

Q7.

	List<Member> list2 = Arrays.asList(
			new Member("홍길동","개발자"),
			new Member("김나리","디자이너"),
			new Member("신용권","개발자")
			);
	List<Member> developers = list2.stream().filter(s->s.getJob().equals("개발자")).collect(Collectors.toList());
	developers.stream().forEach(m->System.out.println(m.getName()));

 

Q8.

 

Map<String, List<String>> groupingMap = list2.stream()
.collect(Collectors.groupingBy(Member::getJob, Collectors.mapping(m->m.getName(), Collectors.toList())));
		
System.out.println("[개발자] ");
groupingMap.get("개발자").stream().forEach(s->System.out.print(s+ " "));
System.out.println("\n[디자이너]");
groupingMap.get("디자이너").stream().forEach(s->System.out.print(s+" "));

'Java > Study' 카테고리의 다른 글

Ch13 제네릭  (0) 2021.04.02
Ch15 확인문제  (0) 2021.04.02
Ch15 컬렉션 프레임워크  (0) 2021.03.31
Ch12. 멀티스레드  (0) 2021.03.29
Ch11 확인문제  (0) 2021.03.29