Java

[이것이 자바다] 자바 스트림 공부 정리

Razelo 2020. 12. 24. 18:07

stream_introduction

package sec01.stream_introduction;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

public class IteratorVsStreamExample {

	public static void main(String[] args) {
		List<String> list = Arrays.asList("홍길동","신용권","감자바");
		
		//자바 7이전에서 사용한 Iterator 이용하기 
		Iterator<String> iterator = list.iterator();
		while(iterator.hasNext()) {
			String name = iterator.next();
			System.out.println(name);
		} 
		
		//Stream 이용하기 -> 자바 8부터 람다식이랑 같이 지원해줌. 
		Stream<String> stream = list.stream();
		stream.forEach(name -> System.out.println(name));
	}

}
package sec01.stream_introduction;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class LamdaExpressionsExample {

	public static void main(String[] args) {
		List<Student> list = Arrays.asList(
			new Student("홍길동",90),
			new Student("신용권",92)
		);
			
		Stream<Student> stream = list.stream();
		stream.forEach(s -> {
			String name = s.getName();
			int score = s.getScore();
			System.out.println(name + "-"+score);
		});

	}

}
package sec01.stream_introduction;

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

public class MapAndReductExample {

	public static void main(String[] args) {
		List<Student> studentList = Arrays.asList(
			new Student("홍길동",10),
			new Student("신용권",20),
			new Student("유미선",30)
		);
		
		//중간 처리 이다. 이때 mapToInt 메소드는 Student를 받아서 int로 바꾼다... 
		//즉 Student매개값을 받아서 getScore해서 점수를 얻어내서 새로운 스트림의 요소로 만들어낸다. 
		//studentList.stream().mapToInt(s -> s.getScore()); 아래와 같은 식이다. 
		double avg = studentList.stream()
				.mapToInt(Student::getScore)   // <-- 이게 중간처리가 된다. 
				.average()                     // <-- 이게 최종처리가 된다. 
				.getAsDouble();
		//(평균값을 구해주는 )average()메소드는 리턴타입이 OptionalDouble이다. 그래서 double로 받아준다.(getAsDouble()) 
		
		System.out.println("평균 점수: "+avg);
	}

}
package sec01.stream_introduction;

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;
	}
	
	
}
package sec01.stream_introduction;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class ParallelExample {

	public static void main(String[] args) {
		List<String> list = Arrays.asList("홍길동","신용권","감자바","람다식","박병렬");
		
		
		//순차 처리 
		Stream<String> stream = list.stream();
		//stream.forEach(name -> print(name)); 메소드 참조를 통해 아래처럼 사용할 수 있다. 
		stream.forEach(ParallelExample::print);
		/*
		홍길동:main
		신용권:main
		감자바:main
		람다식:main
		박병렬:main
		*/
		
		System.out.println();
		//병렬 처리 
		Stream<String> parallelStream = list.parallelStream(); //list로부터 parallelStream()을 통해 병렬 처리가 가능함 스트림을 얻어낸다. 
		parallelStream.forEach(ParallelExample::print);
		
		/*
		감자바:main
		박병렬:main
		신용권:ForkJoinPool.commonPool-worker-3
		람다식:main
		홍길동:ForkJoinPool.commonPool-worker-3 
		 */
		
	}
	public static void print(String str) {
		System.out.println(str + ":"+Thread.currentThread().getName());
	}

}

 

 

 

 

stream_kind

package sec02.stream_kind;

import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class FromArrayExample {

	public static void main(String[] args) {
		String[] strArray = {"홍길동","신용권","김미나"};
		Stream<String> strStream = Arrays.stream(strArray);
		strStream.forEach(a ->System.out.println(a + ","));
		System.out.println();
		
		int[] intArray = {1,2,3,4,5};
		IntStream intStream = Arrays.stream(intArray);
		intStream.forEach(a ->System.out.println(a + ","));
	}

}
package sec02.stream_kind;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class FromCollectionExample {

	public static void main(String[] args) {
		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()));
	}

}
package sec02.stream_kind;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class FromDirectoryExample {

	public static void main(String[] args) throws IOException{
		Path path = Paths.get("C:/Users/user/Desktop/java");
		Stream<Path> stream = Files.list(path);
		stream.forEach(p -> System.out.println(p.getFileName()));
	}
}
Java8에서 추가된 새로운 기능
1. 람다식
2. 메소드 참조
3. 디폴트 메소드와 정적 메소드
4. 새로운 API 패키지 
package sec02.stream_kind;

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;

public class FromFileContentExampld {

	public static void main(String[] args) throws IOException{
		Path path = Paths.get("src/sec02/stream_kind/linedata.txt");
		Stream<String> stream;

		// Files.lines()메소드 이용하는 방법 1
		stream = Files.lines(path, Charset.defaultCharset());
		stream.forEach(System.out::println); // 메서드 참조 방식으로 작성함. 
		//stream.forEach(s -> System.out.println(s)); 람다식으로 이렇게 작성할 수 있음. 
		stream.close();
		System.out.println();
		
		
		// BufferedReader의 lines() 메소드 이용하는 방법 2
		File file = path.toFile();
		FileReader fileReader = new FileReader(file);
		BufferedReader br = new BufferedReader(fileReader);
		stream = br.lines();
		stream.forEach(System.out::println); 
		stream.close();
		System.out.println();
		
	}

}
package sec02.stream_kind;

import java.util.stream.IntStream;

public class FromIntRangeExample {
	public static int sum; //정적 필드 선언 
	public static void main(String[] args) {
		IntStream stream = IntStream.rangeClosed(1,100);
		//1부터 100까지의 (100포함) 수를 가져오는 rangeClosed메서드 
		stream.forEach(a -> sum +=a); //1부터 100까지 실행함. 
		System.out.println("총합: "+sum);
		
	}	

}
package sec02.stream_kind;

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;
	}
	
}

 

 

 

stream_pipelines

package sec03.stream_pipelines;

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

	public int getSex() {
		return sex;
	}
	public int getAge() {
		return age;
	}
	
	
}
package sec03.stream_pipelines;

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

public class StreamPipelinesExample {

	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(); //optionalDouble을 double로 바꿔준다. 
		
		System.out.println("남자 평균 나이: "+ageAvg);
	}

}

 

 

 

 

stream_filtering

package sec04.stream_filtering;

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

public class FilteringExample {

	public static void main(String[] args) {
		List<String> names = Arrays.asList("홍길동","신용권","감자바","신용권","신민철");
		
		//중복이 제거되어 출력된다. 
		names.stream() // <-- stream()해서 얻어낸 건 오리지널 스트림 이라고 부른다. 
		.distinct()
		.forEach(n->System.out.println(n));
		
		System.out.println();
		
		//신으로 시작하는 요소만 가져간다. 
		names.stream()
		.filter(n -> n.startsWith("신")) //이 안에 Predicate 익명 구현 객체를 넣으면 된다. 람다식으로 넣어준다. 
		.forEach(n->System.out.println(n));
		
		System.out.println();
		
		//위에 있는내용을 합침 
		names.stream()
		.distinct()
		.filter(n -> n.startsWith("신"))
		.forEach(n->System.out.println(n));
	
	}

}

 

 

 

 

stream_mapping

package sec05.stream_mapping;

import java.util.Arrays;
import java.util.stream.IntStream;

public class AsDoubleStreamAndBoxedExample {

	public static void main(String[] args) {
		int[] intArray = {1,2,3,4,5};
		
		IntStream intStream = Arrays.stream(intArray);
		intStream.asDoubleStream().forEach(d -> System.out.println(d)); //double 값들이 출력된다. 
		
		System.out.println();
		
		intStream = Arrays.stream(intArray);
		intStream.boxed().forEach(obj -> System.out.println(obj.intValue()));
	
	
	}

}
package sec05.stream_mapping;

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

public class FlatMapExample {

	public static void main(String[] args) {
		List<String> inputList1 = Arrays.asList("java8 lamda","stream mapping");
		inputList1.stream()
			.flatMap(data -> Arrays.stream(data.split(" "))) //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[] strArray = data.split(",");
				int[] intArr = new int[strArray.length];
				for(int i = 0;i<strArray.length;i++) {
					intArr[i] = Integer.parseInt(strArray[i].trim()); //공백있으면 trim()에서 공백을 잘라버림. 
				}
				return Arrays.stream(intArr); //매개로 int배열이 오면 IntStream을 반환하게 된다. 
			}) //중간 스트림으로 IntStream이 만들어진다. 
			.forEach(number -> System.out.println(number));
	}

}
package sec05.stream_mapping;

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

public class MapExample {

	public static void main(String[] args) {
		List<Student> studentList = Arrays.asList(
			new Student("홍길동",10),
			new Student("신용권",20),
			new Student("유미선",30)
		);
		
		studentList.stream()
			.mapToInt(Student::getScore) //<- 메서드 참조 or -> 이렇게 써도 됨. .mapToInt(s-> s.getScore())
			.forEach(score -> System.out.println(score));
	}

}
package sec05.stream_mapping;

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;
	}
	
	
}

 

 

 

 

stream_sorting

package sec06.stream_sorting;

public class Student implements Comparable<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;
	}

	@Override
	public int compareTo(Student o) {
		return Integer.compare(score,o.score);
	}
	
	
	
}
package sec06.stream_sorting;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;


public class SortingExample {

	public static void main(String[] args) {
		IntStream intStream = Arrays.stream(new int[] { 5, 3, 2, 1, 4 }); 
		// 정수 배열에서 바로 Arrays의 stream을 얻어냈으므로
		// IntStream이 된다.

		// 정렬하기
		intStream
		.sorted()
		.forEach(n -> System.out.println(n + ","));
		System.out.println();

		List<Student> studentList = Arrays.asList(
			new Student("홍길동", 10), 
			new Student("신용권", 20),
			new Student("유미선", 30)
		);
		
		//기본정렬하기 student가 가지고 있는 기본 방법대로 정렬을 한다는 뜻임. 
		studentList.stream()
		.sorted()
		.forEach(s -> System.out.println(s.getScore()+","));
		System.out.println();
		
		
		//정렬을 하긴 하는데, 기본비교방법이 아니라 정반대 방법으로 정렬한다. 올림차순이 아니라 내림으로 한다. 
		studentList.stream()
		.sorted(Comparator.reverseOrder())
		.forEach(s -> System.out.println(s.getScore()+","));
		System.out.println();
	}

}

 

 

 

 

 

stream_looping

package sec07.stream_looping;

import java.util.Arrays;

public class LoopingExample {

	public static void main(String[] args) {
		int[] intArr = {1,2,3,4,5};
		
		System.out.println("[peek()를 마지막에 호출한 경우]");
		//아무것도 출력되지 않는다. peek()가 중간처리 메소드이기 때문에 최종처리메소드가 실행되지 않아서 얘는 동작하지 않는다 
		Arrays.stream(intArr)
		.filter(a->a%2==0)
		.peek(n->System.out.println(n)); //동작하지 않음. 
		
		System.out.println("[최종 처리 메소드를 마지막에 호출한 경우]");
		int total =Arrays.stream(intArr)
		.filter(a->a%2==0)
		.peek(n->System.out.println(n))
		.sum(); //필터링한 결과의 합을 구하는 최종처리  /int 타입의 결과 
		System.out.println("총합: "+total);
		
		System.out.println("[forEach()를 마지막에 호출한 경우]");
		Arrays.stream(intArr)
		.filter(a->a%2==0)
		.forEach(n -> System.out.println(n)); //동작함. 최종처리 
	}
}

 

 

 

 

 

stream_match

package sec08.stream_match;

import java.util.Arrays;

public class MatchExample {

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

}

 

 

 

 

 

stream_aggregate

package sec09.stream_aggergate;

import java.util.Arrays;

public class AggregateExample {

	public static void main(String[] args) {
		
		//스트림이 제공하는 기본 집계함수의 사용 
		
		long count = Arrays.stream(new int[] {1,2,3,4,5})
			.filter(n->n%2==0)
			.count(); //count는 리턴타입이 long임
		 System.out.println("2의 배수 개수: "+count);
		 
		 long sum = Arrays.stream(new int[] {1,2,3,4,5})
			.filter(n->n%2==0)
			.sum();
		 System.out.println("2의 배수의 합: "+sum); 
		 
		 int max = Arrays.stream(new int[] {1,2,3,4,5})
			.filter(n->n%2==0)
			.max() //max가 리턴하는 타입은 OptionalInt이다. 
			.getAsInt(); //Optional을 받으려면 이런식으로 받아야한다. 
		System.out.println("최대값: "+max); //2의 배수중 가장 큰 값 4 
		 
		 int min = Arrays.stream(new int[] {1,2,3,4,5})
			.filter(n->n%2==0)
			.min() 
			.getAsInt();  
		System.out.println("최소값: "+min); 
	
		int first = Arrays.stream(new int[] {1,2,3,4,5})
			.filter(n->n%3==0)
			.findFirst() // OptionalInt 리턴함. 
			.getAsInt();
		System.out.println("첫번째 3의 배수: "+first); 
	
	
	}

}
package sec09.stream_aggergate;

import java.util.ArrayList;
import java.util.List;
import java.util.OptionalDouble;

public class OptionalExample {
	
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		
// java.util.NoSuchElementException: No value present
//		double avg = list.stream() //오리지널 스트림을 얻고, 
//				.mapToInt(Integer::intValue) //Integer객체를 intValue메소드를 통해 int값으로 만든다. 
//				.average() //평균을 구한다. 
//				.getAsDouble(); // OptionalDouble을 받아서 double로 만든다. 
//리스트가 비어있어서 에러가 출력된다. 
	
		
		//첫번쨰 방법 
		OptionalDouble optional = list.stream() 
				.mapToInt(Integer::intValue)  //Integer객체를 int값으로 매핑 
				.average(); 
		if(optional.isPresent()) { //값이 있으면 true 
			System.out.println("방법1_평균: "+optional.getAsDouble());
		}else {
			System.out.println("방법1_평균: 0.0");
		}
		
		//두번쨰 방법
		double avg = list.stream() 
			.mapToInt(Integer::intValue) 
			.average()   //OptionalDouble 리턴 
			.orElse(0.0); //OptionalDouble의 orElse(other) 메소드는 결과값이 없을 경우에 other로 값을 정한다.    
		System.out.println("방법2_평균: "+avg);
		
		
		//세번째 방법
		list.add(2); //값이 있으면 밑에 문장이 실행된다. 
		list.add(4);
		list.stream()
		.mapToInt(Integer::intValue)
		.average()
		.ifPresent(a->System.out.println("방법3_평균: "+a));
		//ifPresent에 Consumer로 람다식을 제공했다. average()를 한 이후에 값이 OptionalDouble에 저장되어 있다면 그 a를 출력한다는 뜻
		//근데 average에 결과값이 없으면 false가 되어서 실행하지 않고 그냥 끝난다. 
	}

}


 

 

 

 

stream_reduce

package sec10.stream_reduce;

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;
	}
	
}
package sec10.stream_reduce;

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)
		);
		
		int sum1 = studentList.stream()
				.mapToInt(Student::getScore)
				.sum();
		int sum2 = studentList.stream()
				.mapToInt(Student::getScore)
				.reduce((a,b) -> a+b) //OptionalInt 리턴함. 
				.getAsInt();
		
		int sum3 = studentList.stream()
				.mapToInt(Student::getScore)
				.reduce(0,(a,b) -> a+b); //디폴트 값을 0으로 한다. 
				//얘는 OptionalInt를 리턴하는건가? 아니다. int를 리턴한다. 
				//정의를 보면 returns the reduced value 라고 정의되어 있다. 
				//즉 리덕션된 결과값을 리턴한다. 
		
		
		//단순한 합계라면 그냥 sum()을 이용해라. 복잡한 수식이 필요하다면 reduce()를 이용해라  
		System.out.println("sum1: "+sum1);
		System.out.println("sum2: "+sum2);
		System.out.println("sum3: "+sum3);
		
	}

}

 

 

 

 

 

stream_collect

package sec11.stream_collect;

public class Student {
	public enum Sex{MALE,FEMALE}
	public enum City{Seoul,Pusan}
	
	private String name;
	private int score;
	private Sex sex;
	private City city;
	
	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 String getName() {
		return name;
	}

	public int getScore() {
		return score;
	}

	public Sex getSex() {
		return sex;
	}

	public City getCity() {
		return city;
	}
		
}
package sec11.stream_collect;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ToListExample {

	public static void main(String[] args) {
	 	List<Student> totalList = 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)
		 );
	 	
	 	//남학생들만 묶어 List로 생성
	 	List<Student> maleList = totalList.stream()
	 	.filter(s -> s.getSex() == Student.Sex.MALE)
	 	.collect(Collectors.toList()); //리스트 컬렉션을 생성해서 남학생요소를 저장함. 
	 	//확인하기 
	 	maleList.stream().forEach(s -> System.out.println(s.getName()));
	 	
	 	System.out.println();
	 	
	 	//여학생들만 묶어 HashSet으로 생성 
	 	Set<Student> femaleSet = totalList.stream()
	 	.filter(s->s.getSex() == Student.Sex.FEMALE)
	 	.collect(Collectors.toCollection(HashSet::new));  
	 	//메소드 참조// toCollection이 가지는 매개변수의 타입은supplier이다. 
	 	//그래서 여기에 HashSet을 생성하게 만들었음.
	 	
	 	femaleSet.stream().forEach(s -> System.out.println(s.getName()));
	}	
	 
}
package sec11.stream_collect;

import java.util.ArrayList;
import java.util.List;

public class MaleStudent {
	private List<Student> list;
	
	public MaleStudent() {
		list = new ArrayList<Student>();
		System.out.println("["+Thread.currentThread().getName()+"] MaleStudent()");
	}
	
	public void accumulate(Student student) {
		list.add(student);
		System.out.println("["+Thread.currentThread().getName()+"] accumulate()");
	}
	
	//병렬처리할때 사용할 메서드 
	public void combine(MaleStudent other) { //다른 컨테이너를 매개값으로 받아서 -> other로 받는다. 
		list.addAll(other.getList()); //그 컨테이너의 리스트(내용)을 모두 list에 복사해서 추가한다. -> 결합
		System.out.println("["+Thread.currentThread().getName()+"] combine()");
	}
	
	public List<Student> getList(){
		return list;
	}
}
package sec11.stream_collect;

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

public class MaleStudentExample {

	public static void main(String[] args) {
		List<Student> totalList = 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)
		);
		
		MaleStudent maleStudent = totalList.stream()
		.filter(s -> s.getSex()==Student.Sex.MALE)
		.collect(
				() -> new MaleStudent(), //사용자 정의 컨테이너를 만드는 supplier -> 람다식으로 써줬음. 
				(r,t) -> r.accumulate(t), //BiConsumer이다. r이 컨테이너고(new MaleStudent()를 말함),t는 필터링된 요소 하나를 가리킨다.r컨테이너의 t를 누적을 시키겠다는 뜻. 
				(r1,r2)->r1.combine(r2)); // 지금은 사용되지 않음. 병렬처리를 위해 . 한 객체에서 다른 객체를 결합시키는 기능을 함. 
		//결국 collect는 MaleStudent를 리턴한다. 
		
		maleStudent.getList().stream()
		.forEach(s -> System.out.println(s.getName()));
		
		/* 결과가 이렇게 나온다. 싱글스레드이니 main으로 나온다. 
		 [main] MaleStudent()
		 [main] accumulate()
		 [main] accumulate()
		 홍길동
		 신용권
		 */
		//여기서 accumulate()가 두번 출력된 것을 볼 수 있는데, 필터링한 요소가 두개이기 때문에 두번 나온 것이다. (홍길동과 신용권 ) ->  2번 
		//객체 하나가 들어와서 추가가 되고 1 , 또 하나가 들어와서 추가가 됨.  2 
		//필터 메서드에서 생성된 스트림의 요소수만큼 accumulate가 실행이 된다. 

	}

}
package sec11.stream_collect;

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

public class GroupingExample {

	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.Pusan),
			new Student("신용권",10,Student.Sex.MALE,Student.City.Pusan),
			new Student("박수미",6,Student.Sex.FEMALE,Student.City.Seoul)
		);
		
		Map<Student.Sex,List<Student>> mapBySex = totalList.stream()
				.collect(Collectors.groupingBy(Student::getSex));
		
		System.out.print("[남학생]");
		mapBySex.get(Student.Sex.MALE).stream()//해당 키로 저장된 리스트를 얻어내고, 스트림을 얻어낸다. 
			.forEach(s->System.out.print(s.getName()+ " ")); //스트림의 요소를 하나씩 출력한다. 
		
		System.out.print("\n[여학생]");
		mapBySex.get(Student.Sex.FEMALE).stream()//해당 키로 저장된 리스트를 얻어내고, 스트림을 얻어낸다. 
		.forEach(s->System.out.print(s.getName()+ " ")); //스트림의 요소를 하나씩 출력한다. 
		
		System.out.println();
		
		Map<Student.City,List<String>> mapByCity = totalList.stream()
				.collect(
						Collectors.groupingBy(
							Student::getCity,
							Collectors.mapping(Student::getName, Collectors.toList())
						)
				);
		System.out.print("\n[서울]");
		mapByCity.get(Student.City.Seoul).stream()
		.forEach(name -> System.out.print(name+ " "));
	
		System.out.print("\n[부산]");
		mapByCity.get(Student.City.Pusan).stream()
		.forEach(name -> System.out.print(name+ " "));
		
	}

}
package sec11.stream_collect;

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

public class GroupingAndReductionExample {

	public static void main(String[] args) {
		List<Student> totalList = 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)
		);
		
		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<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));
		
	}

}

 

 

 

 

 

필기

 

package Stream;

/*
 2020.12.23 
 <스트림>
 
 스트림 소개
 스트림의 종류
 스트림 파이프라인
 필터링
 매핑
 정렬
 루핑
 매칭
 기본 집계
 커스텀 집계
 수집
 병렬 처리
 순으로 진행됩니다... 

<스트림은 반복자>
컬렉션(배열 포함)의 요소를 하나씩 참조해서 람다식으로 처리할 수 있는 반복자이다. 

<스트림 특징 >
람다식으로 요소 처리 코드를 제공한다.
스트림이 제공하는 대부분의 요소 처리 메소드는 함수적 인터페이스 매개타입을 가진다. 
이 말은 즉슨... 매개값으로 람다식 또는 메소드 참조를 대입할 수 있다... ㄴ
Tip:  forEach(Consumer<? super T> action) 형식이다. 

<스트림 -> 내부 반복자를 사용하므로 병렬 처리가 쉽다. >
<외부 반복자 (external iterator)>
개발자가 코드로 직접 컬렉션 요소를 반복해서 요청하고 가져오는 코드 패턴

<내부 반복자(internal iterator)>
컬렉션 내부에서 요소들을 반복시키고 개발자는 요소당 처리해야할 코드만 제공하는 코드 패턴

<내부 반복자의 이점>
개발자는 요소 처리 코드에만 집중
멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 처리 작업을 할 수 있다. 

<병렬(Parallel) 처리>
 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리한 후, 
 서브 작업들의 결과들을 최종 결합하는 방법
  자바는 ForkJoinPool 프레임워크를 이용해서 병렬 처리를 한다.  
  
 스트림은 중간 처리와 최종 처리를 할 수 있다. 
 중간 처리: 요소들의 매핑, 필터링, 정렬
 최종 처리: 반복, 카운트, 평균, 총합 
  
  
스트림이 포함된 패키지
BaseStream: 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어 있을 뿐 
코드에서 직접적으로 사용되지는 않는다. 

Stream: 객체 요소를 처리하는 스트림
 
 
 파일로부터 스트림 얻기 .. 
  
 디렉토리로부터 스트림 얻기. 
  
 ------------------------------------------------------
 <중간 처리와 최종 처리>
 리덕션(Reduction) -> 
 대량의 데이터를 가공해서 축소하는 것을 말한다. 
 합계, 평균값, 카운팅, 최대값, 최소값 등을 집계 하는 것 
 
 요소가 리덕션의 결과물로 바로 집계할 수 없을 경우 중간 처리가 필요하다. 
 중간처리: 필터링, 매핑, 정렬, 그룹핑
 
 중간 처리한 요소를 최종 처리해서 리덕션 결과물을 산출한다. 
 
 스트림은 중간 처리와 최종 처리를 파이프라인(pipelines)으로 해결한다. 
 파이프라인: 스트림을 파이프처럼 이어 놓은 것을 말한다. 
 중간 처리 메소드(필터링,매핑,정렬)는 중간 처리된 스트림을 리턴하고
 이 스트림에서 다시 중간 처리 메소드를 호출해서 파이프라인을 형성하게 된다. 
 
 스트림소스(컬렉션,배열,파일) -> 오리지날 스트림 -> 필터링 처리 중간 스트림 -> 매핑 처리 중간 스트림 -> 집계 처리 결과물  -> 결과물
--> 
 최종 스트림의 집계 기능이 시작되기 전까지 중간 처리는 지연(lazy) 된다. 
 최종 스트림이 시작하면 비로소 컬렉션에서 요소가 하나씩 중간 스트림에서 처리되고, 최종 스트림까지 오게 된다. 
 
 <중간처리 메소드와 최종 처리 메소드>
 리턴 타입을 보면 중간 처리 메소드인지 최종 처리 메소드인지 구분할 수 있다. 
 중간 처리 메소드: 리턴 타입이 스트림
 최종 처리 메소드: 리턴 타입이 기본 타입이거나 OptionalXXX
 
 중간 처리 메소드 
 필터링: 요소를 추려낸다.
 distinct() 중복을 제거한다. 
 filter() 조건에 맞는 것만 필터링 
 최종처리 메소드가 시작을 해야만 중간 처리 메소드가 작동한다. 
 
 최종처리 메소드 
 reduce() 사용자가 정의한 값을 집계함. 
 sum() 합을 구함. 
 collect() 요소들을 별도의 컬렉션으로 수집할 떄 사용 
 
 -----------------------------------------------
 <필터링>
 distinct()
 Stream: equals() 메소드가 true가 나오면 동일한 객체로 판단하고 중복을 제거
 IntStream,LongStream,DoubleStream: 동일값일 경우 중복을 제거 
 
 filter()
 매개값으로 주어진 Predicate가 true를 리턴하는 요소만 필터링 
 
 
 
 
 
 <매핑>
 매핑(mapping)은 중간 처리 기능으로 스트림의 요소를 다른 요소로 대체한다. 
 매핑 메소드 종류
 flatMapXXX()와 mapXXX(), asXXXStream(), boxed()
 
 flatMapXXX()메소드
 한 개의 요소를 대체하는 복수개의 요소들로 구성된 새로운 스트림을 리턴한다. (복수의 요소로 대체)
 
 mapXXX() 메소드 
 요소를 대체하는 요소로 구성된 새로운 스트림을 리턴한다. (하나의 요소로 대체)
 
 asDoubleStream() asLongStream() boxed() 메소드 
asDoubleStream()
  IntStream의 int요소 또는 LongStream의 long요소를 double 요소로 
  타입 변환해서 DoubleStream을 생성 
  
asLongStream()
  IntStream의 int 요소를 long요소로 타입 변환해서 LongStream을 생성
  
boxed()
int요소, long요소, double 요소를 Integer, Long, Double 요소로 박싱해서 Stream을 생성 




<정렬>
sorted()
중간 처리 기능으로 최종 처리되기 전에 요소를 정렬한다. 

객체 요소일 경우에는 Comparable을 구현하지 않으면 첫번쨰 sorted() 메소드를 호출하면
ClassCastException이 발생

객체 요소가 Comparable을 구현하지 않았거나, 구현 했더라도 다른 비교 방법으로 정렬
하려면 Comparator을 매개값으로 가는 두번째 sorted()메소드를 사용 
-> Comparator는 함수적 인터페이스이므로 람다식으로 매개값을 작성할 수 있다. 
-> 중괄호 {}안에는 a와 b를 비교해서 a가 작으면 음수, 같으면 0, a가 크면 양수를 리턴하는 코드를 작성한다. 





<루핑>
중간 또는 최종 처리 기능으로 요소 전체를 반복하는 것을 말한다. 
루핑 메소드 
peek()메소드 -> 중간 처리 메소드 
최종 처리 메소드가 실행되지 않으면 지연되기 때문에 최종 처리 메소드가 호출되어야만 동작한다. 

forEach()메소드 -> 최종 처리 메소드 




<매칭>
최종 처리 기능으로 요소들이 특정 조건을 만족하는지 조사하는 것을 말한다. 
매칭 메소드

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

anyMatch()
최소한 한 개의 요소가 매가값으로 주어진 Predicate의 조건을 만족하는지 조사

noneMatch()
모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하지 않는지 조사 




<집계 Aggregate>
최종 처리 기능
카운팅,합계,평균값,최대값,최소값 등과 같이 하나의 값으로 산출한다. 
대량의 데이터를 가공해서 축소하는 리덕션(Reduction)이라고 볼 수 있다. 

OptionalXXX 클래스
자바8부터 추가된 값을 저장하는 값 기반 클래스
java.util 패키지의 Optional,OptionalDouble, OptionalInt,OptionalLong 클래스를 말한다. 
저장된 값을 얻을려면, get().getAsDouble(), getAsInt(), getAsLong()


 

<Optional클래스>
값을 저장하는 값 기반 클래스 
Optional,OptionalDouble, OptionalInt,OptionalLong
집계 메소드의 러턴 타입으로 사용되어 집계 값을 가지고 있음.

특징:
집계  값이 존재하지 않을 경우 디폴트 값을 설정할 수도 있다. 
집계 값을 처리하는 Consumer 를 등록할 수 있다. 

-------------------------------------------------------------
<parseInt()와 intValue()이 차이>
parseInt():
static이므로 Integer 생성 안하고, parameter 만 넣어주면 
메소드를 수행할 수 있다. 
String형 객체에서 int형 값을 뽑아내는 메소드이다. 
문자형을 정수형으로 만든다. 
int i = Integer.parseInt(str);


intValue():
static이 아니며, Integer 객체에서 int형 값을 뽑아내는 메소드이다.
Integer는 (int Value와 String Value) 두 가지가 있다. 
int i = Integer.valueOf(str).intValue();
or 
Integer i = new Integer(200);
i.intValue()
--------------------------------------------------------------

<커스텀 집계>
reduce()메소드 
프로그램화해서 다양한 집계(리덕션) 결과물을 만들 수 있따. 

매개변수 
XXXBinaryOperator: 두 개의 매개값을 받아 연산 후 리턴하는 함수적 인터페이스 
ientity: 스트림에 요소가 전혀 없을 경우 리턴될 디폴트 값 


<수집 - collect()>
최종 처리  기능으로 요소들을 수집 또는 그룹핑한다. 
필터링 또는 매핑된 요소들로 구성된 새로운 컬렉션 생성한다. 
요소들을 그룹핑하고, 집계(리덕션)을 할 수 있다. 
 
필터링한 요소 수집
리턴타입 		메소드(매개변수) 								인터페이스 
R   		collect(Collector<T,A,R> collector)  		Stream

Collector의 타입 파라미터
T : 요소 
A : 누적기(accumulator)
R : 요소가 저장될 새로운 컬렉션

Collector의 구현 객체
Collectors 클래스의 정적 메소드를 이용함. 

A(누적기)가 ? 인 이유
List,Set,Map 컬렉션에 누적될 경우에는 별도의 A(누적기)가 필요 없다.  


<사용자 정의 컨테이너에 수집하기>
List,Set,Map에 수집하는 것이 아니라 사용자 정의 컨테이너에 수집시키는 것을 말한다. 

<예시 >
인터페이스 		리턴타입			메소드(매개변수)
Stream			R				collect(Supplier<R>, Biconsumer<R,? super T>, Biconsumer<R,R>)

[매개변수]
첫번째 Supplier: 요소들이 수집될 컨테이너 객체를 생성하는 역할
순차 처리(싱글 스레드) 스트림: 단 한 번 Supplier 가 실행
병렬 처리(멀티 스레드) 스트림: 스레드별로 Supplier가 실행되어 스레드별로 컨테이너가 생성

두번째 XXXConsumer: 컨테이너 객체에 요소를 수집하는 역할
스트림에서 요소를 컨테이너에 누적할 때마다 실행

세번째 BiConsumer: 컨테이너 객체를 결합하는 역할
순차 처리(싱글 스레드) 스트림: 실행되지 않음
병렬 처리(멀티 스레드) 스트림:  스레드별로 생성된 컨테이너를 결합해서 최종 컨테이너를 완성한다. 

[리턴타입]
R: 최종 누적된 컨테이너 객체 
    

<요소를 그룹핑해서 수집>
 collect() 메소드는 단순히 요소를 수집하는 기능 이외에 컬렉션의 요소들을 그룹핑해서
 Map객체로 생성하는 기능도 제공한다. 
 
 Collectors.groupingBy() 의 리턴 객체를 매개값으로 대입 
 -> 스레드에 안전하지 않는 Map 생성
 
 Collectors.groupingByConcurrent()의 리턴 객체를 매개값으로 대입
 -> 스레드에 안전한 ConcurrentMap 생성 


<그룹핑 후 매핑 및 집계(리덕션)>
Collectors.groupingBy()메소드는 그룹핑 후,
매핑이나 집계(평균, 카운팅, 연결, 최대 , 최소, 합계)을 할 수 있도록 하기 위해
두번째 매개값으로 다음과 같은 Collector를 가질 수 있다. 




*/
반응형