본문 바로가기

자바(JAVA)
[자바(Java)] 스트림(stream)

※함수형 프로그래밍(스트림 사용) vs 일반 절차적 프로그래밍

1.  함수형 프로그래밍에서는 변수 변이를 피한다.
    > 값이 변하는 변수를 굳이 넣지 않는다.

//함수형
private static int fpSum(List<Integer> numbers) {
	return numbers.stream().reduce(0, (number1, number2) -> number1 + number2);
}

//절차적
private static int normalSum(List<Integer> numbers) {
	int sum = 0;
    for(int number:number) {
    	sum += numbers;
    }
    return sum;
}


2. 함수형 프로그래밍은 무엇을 할지에 중점을 둔다.


// 스트림(stream)

- 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것

Stream<Integer> intStream = list.stream(); //컬렉션
Stream<String> strStream = Stream.of(new String[]{"a", "b", "c"}); //배열
Stream<Double> randomStream = Stream.generate(Math::random); //람다식
Stream<Integer> evenStream = Stream.iterate(0, n-> n+2); //람다식
IntStream intStream = new Random().ints(5); //크기가 5인 난수 스트림


- 데이터 소스로부터 데이터를 읽기만 할 뿐, 변경하지 않음!(원본은 그대로)

- Iterator처럼 일회용. 필요시 다시 스트림을 생성해야 함.

- 지연된 연산: 최종 연산 전까지 중간 연산이 수행되지 않는다.

- 작업을 내부 반복으로 처리함.

for(String str : strList) {
	System.out.println(str);
}

//stream
stream.forEach(System.out::println);


- 병렬 스트림: 스트림의 작업을 병렬로 처리함.(멀티 쓰레드)

- 기본형 스트림- IntStream, LongStream, DoubleStream
    ~ 오토박싱&언박싱의 비효율이 제거됨. (Stream<Integer> 대신 IntStream 사용)
    ~ 숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공함.


// 스트림 만들기

- Collection 인터페이스의 stream()으로 컬렉션을 스트림으로 변환

Stream<E> stream() 

- 객체 배열로부터 스트림 생성

Stream<T> Stream,of(T...values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)

- 기본형 배열로부터 스트림 생성

IntStream IntStream,of(int...values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)


- 난수를 요소로 갖는 스트림 생성

IntStream intStream = new Random().ints() // 무한 스트림
intStream.limit(n).forEach() //n개의 요소 출력

IntStream intStream = new Random().ints(n)  
intStream.lforEach()


- 지정된 범위의 난수를 요소로 갖는 스트림 생성(Random클래스)

~ 무한 스트림
IntStream ints(int begin, int end)
LongStream longs(long begin, long end)
DoubleStream doubles(double begin, double end)

~ 유한 스트림
IntStream ints(long streamSize, int begin, int end)
LongStream longs(long streamSize, long begin, long end)
DoubleStream doubles(long streamSize, double begin, double end)


- 특정 범위의 정수를 요소로 갖는 스트림 생성(IntStream, LongStream)

IntStream intStream = intStream.range(int begin, int end)
IntStream intStream = intStream.rangeClosed(int begin, int end) // end도 포함

- 람다식을 소스로 하는 스트림 생성(무한 스트림)

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) // 이전 요소에 종속적(이전 요소를 seed로 해서 다음 요소를 계산함.)
static<T>  Stream<T> generate(Supplier<T> s)


- 파일을 소스로 하는 스트림 생성

Stream<Path> Files.list(Path dir) //Path는 파일 또는 디렉토리

Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // bufferedReader 클래스의 메서드


- 비어 있는 스트림 생성

Stream emptyStream = Stream.empty();


- Collection 인터페이스

List<Integer> list= Arrays.asList{1,2,3,4,5};
Stream<Integer> intStream = list.stream(); //list를 스트림으로 변환

intStream.forEach(System.out::print);

- 객체/기본형 배열로부터 스트림

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

public class Ex09 {
	public static void main(String[] args) {
		Stream<String> strStream1 = Stream.of("a", "b", "c");
		strStream1.forEach(System.out::println);
		
		Stream<String> strStream2 = Stream.of(new String[] {"A", "B", "C", "D"});
		strStream2.forEach(System.out::println);
		
		Stream<String> strStream3 = Arrays.stream(new String[] {"a", "b", "c", "d"});
		strStream3.forEach(System.out::println);
		
		int[] intArr = {1,2,3,4,5};
		IntStream intStream = Arrays.stream(intArr);
		intStream.forEach(System.out::println);
		
		intStream = Arrays.stream(intArr);
		System.out.println("count = " + intStream.count());
		
		intStream = Arrays.stream(intArr);
		System.out.println("sum = " + intStream.sum());
		
		intStream = Arrays.stream(intArr);
		System.out.println("average = " + intStream.average());
	}
}

- 난수를 요소로 갖는 스트림

import java.util.Random;
import java.util.stream.IntStream;

public class Wz10 {
	public static void main(String[] args) {
		IntStream intStream1 = new Random().ints();
		intStream1.limit(5).forEach(System.out::println);
		System.out.println();
		
		IntStream intStream2 = new Random().ints(5);
		intStream2.forEach(System.out::println);
		System.out.println();
		
		IntStream intStream3 = new Random().ints(0, 11);
		intStream3.limit(5).forEach(System.out::println);
		System.out.println();
	}
}

- 특정 범위의 정수를 요소로 갖는 스트림

import java.util.stream.IntStream;

public class Ex11 {
	public static void main(String[] args) {
		IntStream intStream1 = IntStream.range(0, 10);
		intStream1.forEach(System.out::print);
		System.out.println();
		
		IntStream intStream2 = IntStream.rangeClosed(0,10);
		intStream2.forEach(System.out::print);
	}
}

- 람다식을 소스로 하는 스트림

import java.util.stream.Stream;

public class Ex12 {
	public static void main(String[] args) {
		Stream<Integer> intStream = Stream.iterate(0, n -> n + 2);
		intStream.limit(10).forEach(System.out::println);
		
		Stream<Integer> oneStream = Stream.generate(() -> 1);
		oneStream.limit(10).forEach(System.out::println);
	}
}

// 중간 연산 : 연산 결과가 스트림인 연산. 반복적으로 적용 가능.

- 중복 제거

Stream<T> distinct() : 중복 제거

- 조건에 안 맞는 요소 제외

Stream<T> filter(Predicate<T> predicate)

- 스트림의 일부를 잘라냄.

Stream<T> limit(long maxSize)

- 스트림의 일부를 건너뜀.

Stream<T> skip(long n)

- 스트림의 요소에 작업 수행

Stream<T> peek(Consumer<T>)

- 스트림 요소 정렬

Stream<T> sorted()
Stream<T> sorted(Comparator<T> comparator)

strtStream.sorted();
strtStream.sorted((s1,s2) -> s1.compareTo(s2));
strtStream.sorted(String::compareTo);

StrStream.sorted(Comparator.reverseOrder()) //역순 정렬

strtStream.sorted(String.CASE_INSENSITIVE_ORDER) //대소문자 구분x
strtStream.sorted(String.CASE_INSENSITIVE_ORDER.reversed()) //대소문자 구분x, 역순 정렬

strtStream.sorted(Comparator.comparing(String::length)) //길이순
strtStream.sorted(Comparator.comparing(String::length).reversed()) //길이순, 역순 정렬


~ Comparator의 comparing()으로 정렬 기준을 제공

comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)


~ 추가 정렬 기준을 제공할 때는 thenCompoarint() 사용 

thenComparing(Comparator<T> other)
thenComparing(Function<T, U> keyExtractor)
thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)



- 스트림 요소 변환

Stream<R> map(Function<T,R> mapper)
DoubleStream mapToDouble(ToDoubleFunction<T> mapper)
IntStream mapToInt(ToIntFunction<T> mapper)
LongStream mapToLong(ToLongFunction<T> mapper)

Stream<R> flatmap(Function<T,Stream<R>> mapper)
DoublieStream flatmap(Function<T,DoubleStream> m)
DoublieStream flatmap(Function<T,IntStream> m)
DoublieStream flatmap(Function<T,LongStream> m)


 

- skip(), limit(), filter(), distinct()

import java.util.stream.IntStream;

public class Ex14 {
	public static void main(String[] args) {
		IntStream intStream = IntStream.rangeClosed(1, 10);
		intStream.skip(3).limit(5).forEach(System.out::print);
		System.out.println();
		
		intStream = IntStream.of(1,2,2,3,3,3,4,5,5);
		intStream.distinct().forEach(System.out::print);
		System.out.println();
		
		intStream = IntStream.rangeClosed(1, 10);
		intStream.filter(i -> i%2 == 0).forEach(System.out::print);
	}
}

- sorted()

import java.util.Comparator;
import java.util.stream.Stream;

public class Ex13 {
	public static void main(String[] args) {
		Stream<Student> studentStream = Stream.of(
						new Student("이자바", 3, 300),
						new Student("한자바", 1, 200),
						new Student("손자바", 2, 100),
						new Student("조자바", 2, 150),
						new Student("정자바", 1, 180),
						new Student("황자바", 3, 290),
						new Student("권자바", 3, 180)
					);
		studentStream.sorted(Comparator.comparing(Student::getBan) //1. 반별 정렬/\
        //studentStream.sorted(Comparator.comparing((Student s) -> s.getban())
				.thenComparing(Comparator.naturalOrder())) //2. 기본 정렬
				.forEach(System.out::println);
	}
}

class Student implements Comparable<Student> {
	String name;
	int ban;
	int totalScore;
	
	Student(String name, int ban, int totalScore) {
		this.name = name;
		this.ban = ban;
		this.totalScore = totalScore;
	}
	
	public String toString() {
		return String.format("[%s, %d, %d]", name, ban, totalScore);
	}
	
	String getName() { return name; }
	int getBan() { return ban; }
	int getTotalScore() { return totalScore; }
	
	public int compareTo(Student s) { // 총점 내림차순을 기본 정렬로 한다.
		return s.totalScore - this.totalScore;
	}
}


- sorted().reversed()

import java.util.Comparator;
import java.util.stream.Stream;

public class Ex13 {
	public static void main(String[] args) {
		Stream<Student> studentStream = Stream.of(
						new Student("이자바", 3, 300),
						new Student("한자바", 1, 200),
						new Student("손자바", 2, 100),
						new Student("조자바", 2, 150),
						new Student("정자바", 1, 180),
						new Student("황자바", 3, 290),
						new Student("권자바", 3, 180)
					);
		studentStream.sorted(Comparator.comparing(Student::getBan).reversed() 
				.thenComparing(Comparator.naturalOrder()).reversed()) 
				.forEach(System.out::println);
	}
}

class Student implements Comparable<Student> {
	String name;
	int ban;
	int totalScore;
	
	Student(String name, int ban, int totalScore) {
		this.name = name;
		this.ban = ban;
		this.totalScore = totalScore;
	}
	
	public String toString() {
		return String.format("[%s, %d, %d]", name, ban, totalScore);
	}
	
	String getName() { return name; }
	int getBan() { return ban; }
	int getTotalScore() { return totalScore; }
	
	public int compareTo(Student s) { // 총점 내림차순을 기본 정렬로 한다.
		return s.totalScore - this.totalScore;
	}
}

- map()

import java.io.File;
import java.util.stream.Stream;

public class Ex15 {	
	public static void main(String[] args) {
		File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"), 
				new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
		};
		
		Stream<File> fileStream = Stream.of(fileArr);
		
		//map()으로 Stream<File>을 Stream<String>으로 변환
		Stream<String> filenameStream = fileStream.map(File::getName);
		filenameStream.forEach(System.out::println); //모든 파일 이름 출력
		System.out.println();
		
		fileStream = Stream.of(fileArr);
		
		fileStream.map(File::getName) //Stream<File> -> Stream<String>
			.filter(s -> s.indexOf('.') != -1) //확장자 없는 것은 제외
			.map(s -> s.substring(s.indexOf('.') + 1)) //확장자만 추출
			.map(String::toUpperCase) //대문자로 변환
			.distinct() //중복 제거
			.forEach(System.out::print); //JAVABAKTXT
	}
}

package com.sorrel012.functionalprogramming;

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

public class IntermediateOperation {
	
	public static void main(String[] args) {

		//1~10까지 제곱값 구하기
		IntStream.range(1, 11).map(e -> e * e).forEach(System.out::println);
		System.out.println();
		System.out.println();
		
		//소문자로 매핑하여 출력하기
		List.of("Apple", "Ant", "Bat").stream().map(String::toLowerCase)
												.forEach(System.out::println);
		System.out.println();
		System.out.println();
		
		//문자열 길이 출력하기
		List.of("Apple", "Ant", "Bat").stream().map(String::length)
												.forEach(System.out::println);
		System.out.println();
		System.out.println();
		
	} //main
}

- peek()

import java.io.File;
import java.util.stream.Stream;

public class Ex15 {	
	public static void main(String[] args) {
		File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"), 
				new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
		};
		
		Stream<File> fileStream = Stream.of(fileArr);
		
		Stream<String> filenameStream = fileStream.map(File::getName);
		filenameStream.forEach(System.out::println);
		System.out.println();
		
		fileStream = Stream.of(fileArr);
		
		fileStream.map(File::getName) 
			.filter(s -> s.indexOf('.') != -1) 
			.peek(s -> System.out.printf("filename = %s\n", s))
			.map(s -> s.substring(s.indexOf('.') + 1)) 
			.peek(s -> System.out.printf("extension = %s\n", s))
			.map(String::toUpperCase) 
			.distinct() 
			.forEach(System.out::println);
	}
}

- flatMap()

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

public class Ex16 {
	public static void main(String[] args) {
		Stream<String[]> strArrStream = Stream.of(
			new String[] {"abc", "def", "jkl"},
			new String[] {"ABC", "GHI", "JKL"}
		);
		
		Stream<String> strStream = strArrStream.flatMap(Arrays::stream);
		
		strStream.map(String::toLowerCase)
				.distinct()
				.sorted()
				.forEach(System.out::println);
		System.out.println();
		
		String[] lineArr = {
			"Believe or not It is true",
			"Do or do not There is no try"
		};
		
		Stream<String> lineStream = Arrays.stream(lineArr);
		lineStream.flatMap(line -> Stream.of(line.split(" +")))
			.map(String::toLowerCase)
			.distinct()
			.sorted()
			.forEach(System.out::println);
		System.out.println();
	}
}

// 최종 연산 : 연산 결과가 스트림이 아닌 연산. 단 한 번만 적용 가능.(스트림의 요소를 소모)

- 각 요소에 지정된 작업 수행

void forEach(Consumer<? super T> action)
void forEachOrdered(Consumer<? super T> action) //병렬처리 할 때 순서 유지

    ★★★forEach 메서드의 행동
        1. stream  > 배열(컬렉션)으로부터 데이터 집합을 공급받는다.
        2. forEach  > 공급받은 데이터를 하나씩 꺼내서 Consumer에 전달한다.
        3. Consumer > forEach가 하나씩 꺼내 온 데이터를 전달 받아 처리한다.

- 스트림의 요소 개수 반환

long count()

- 스트림의 최댓값/최솟값 반환

Optional<T> max(Comparator<? super T> comparator) //병렬처리 할 때 filter()와 함께 쓰임.
Optional<T> min(Comparator<? super T> comparator)

- 스트림의 요소 하나 반환

Optional<T> findAny //아무거나 하나
Optional<T> findFirst() //첫 번째 요소

- 주어진 조건을 모든 요소가 만족시키는지, 만족시키지 않는지 확인

boolean allMatch(Predicate<T> p) //모두 만족?
boolean anyMatch(Predicate<T> p) //하나라도 만족?
boolean noneMatch(Predicate<T> p) //모두 만족x?

- 스트림의 모든 요소를 배열로 반환

Object[] toArray()
A[] toArray(InFunction<A[]> generator)

- 스트림의 요소를 하나씩 줄여가면서 계산(리듀싱)

Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(T identity, BinaryFunction<U,T,U> accumulator, BinaryOperator<U> combiner)

    ~ identity : 초기값
    ~ accumulator : 이전 연산 결과와 스트림의 요소에 수행할 연산

- 스트림의 요소 수집. 주로 요소를 그룹화하거나 분할한 결과를 컬렉션에 담아  반환하는 데 사용.

R collect(Collector<T,A,R> collector)
R collect(Supplier<R> supplier, BiConsumer<R,T> accumulator, BiConsumer<R,R> combiner)


import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Ex19 {
	public static void main(String[] args) {
		String[] strArr = { "Inheritance", "Java", "Lambda", "Stream",
							"OptionalDouble", "IntStream", "count", "sum" };
		
		Stream.of(strArr).forEach(System.out::println);
		System.out.println();
		
		boolean noEmptyStr = Stream.of(strArr).noneMatch(s -> s.length() == 0);
		Optional<String> sWord = Stream.of(strArr).filter(s -> s.charAt(0) == 's').findFirst();
		
		System.out.println("noEmptyStr = " + noEmptyStr);
		System.out.println("sWord = " + sWord.get() + "\n");
		
        //Stream<String>을 IntStream으로 변환 {11,4,6,6,14,9,5,3}
		IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
		IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
		IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
		IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);
		
		int count = intStream1.reduce(0, (a,b) -> a+1);
		int sum = intStream2.reduce(0, (a,b) -> a+b);
		OptionalInt max = intStream3.reduce(Integer::max);
		OptionalInt min = intStream4.reduce(Integer::min);
		
		System.out.println("count = " + count);
		System.out.println("sum = " + sum);
		System.out.println("max = " + max.getAsInt());
		System.out.println("min = " + min.getAsInt());
	}
}

package com.sorrel012.functionalprogramming;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class TerminalOperation {
	
	public static void main(String[] args) {

		List<Integer> list = List.of(23,12,34,53);
		System.out.println("원본 리스트: " + list);
		
		int max = list.stream().max(Integer::compare).get();
		System.out.println("최댓값: " + max);
		
		int min = list.stream().min(Integer::compare).get();
		System.out.println("최솟값: " + min);
		
		int sum = IntStream.range(1, 11).reduce(0, (n1,n2) -> n1+n2);
		System.out.println("1~10까지의 총합: " + sum);
		
		List<Integer> oddList = list.stream().filter(e -> e%2 == 1).collect(Collectors.toList());
		System.out.println("홀수들만: " + oddList);

	} //main
}

//Optional<T>

- T 타입 객체의 래퍼 클래스

public final class Optional<T> {
	private final T value; // T타입의 참조 변수
    		...
}


- Optional<T> 객체 생성 방법

String str = "abc";
Optional<>String optVal = Optional.of(str);
Optional<>String optVal = Optional.of("abc");
Optional<>String optVal = Optional.ofNullable(null);


- 간접적으로 null을 다루기 위함. (null 대신 빈 Optional<T> 객체를 사용하자!)

- Optional 객체의 값 가져오기 - get(), orElse(), orElseGet(), orElseThrow()

Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); //optVal에 저장된 값 반환. null이면 예외 발생
String str2 = optVal.orElse(""); //optVal에 저장된 값이 null일 때는 ""를 반환
String str3 = optVal.orElseGet(String::new); //람다식 사용 가능 () -> new String()
String str4 = optVal.orElseThrow(NullPointerException::new) //null이면 예외 발생


- Optional 객체의 값이 null이면 false, 아니면 true를 반환 - isPresent()


import java.util.Optional;

public class Ex17 {
	public static void main(String[] args) {		
		Optional<String> opt = Optional.empty();
		System.out.println("opt = " + opt);
		
		String str = "";
		try {
			str = opt.get();
		} catch (Exception e) {
			str = ""; // 예외가 발생하면 빈문자열("")로 초기화
		}
		System.out.println("str = " + str);
		
		str = opt.orElse("EMPTY");
		System.out.println("str = " + str);
		
		str = opt.orElseGet(String::new);
		System.out.println("str = " + str);
	}
}

package com.sorrel012.functionalprogramming;

import java.util.List;

public class TerminalOperation {
	
	public static void main(String[] args) {
		
		int max = List.of(23,12,53).stream().filter(n -> n%2 == 0).max(Integer::compare).orElse(0);
		System.out.println(max);
		
		int max2 = List.of(23,13,53).stream().filter(n -> n%2 == 0).max(Integer::compare).orElse(0);
		System.out.println(max2);
		
	} //main
}

// OptionalInt, OptionalLong, OptionalDouble

- 기본형 값을 감싸는 래퍼클래스

- 성능을 올리기 위해 사용.

- 값 가져오기

OptionalInt - int getAsInt()
OptionalLong - long getAsLong()
OptionalDouble - double getAsDouble()

- 빈 Optional 객체와의 비교 - isPresent(), equals()


import java.util.Optional;
import java.util.OptionalInt;

public class Ex18 {
	public static void main(String[] args) {
		Optional<String> optStr = Optional.of("abcde");
		Optional<Integer> optInt = optStr.map(s -> s.length());
		System.out.println("optStr = " + optStr.get());
		System.out.println("optInt = " + optInt.get());
		
		int result1 = Optional.of("123")
							.filter(x -> x.length() > 0)
							.map(Integer::parseInt).get();
		
		int result2 = Optional.of("")
				.filter(x -> x.length() > 0)
				.map(Integer::parseInt).orElse(-1);
		
		System.out.println("result1 = " + result1);
		System.out.println("result2 = " + result2);
		
		Optional.of("456").map(Integer::parseInt)
						.ifPresent(x -> System.out.printf("result3 = %d\n", x));
		
		OptionalInt optInt1 = OptionalInt.of(0); // 0을 저장
		OptionalInt optInt2 = OptionalInt.empty(); // 0을 저장
		
		System.out.println("optInt1.isPresent() = " + optInt1.isPresent());
		System.out.println("optInt2.isPresent() = " + optInt2.isPresent());
	}
}

// collect()

- Collector를 매개변수로 하는 스트림의 최종 연산

Object collect(Collector collector)

    ~ Collector는 수집에 필요한 메서드를 정의해 놓은 인터페이스

※ reduce vs collect
- reduce: 스트림 요소 전체 리듀싱
- collect : 그룹별 리듀싱

- Collectors 클래스는 다양한 기능의 컬렉터를 제공

    ~ 변환 - mapping(), toList(), toSet(), toMap(), toCollection()

List<String> names = stuStream.map(Student::getName) //Stream<Student> -> Stream<String>
                      .collect(Collectors.toList()); //Stream<String> -> List<String>

ArrayList<String> list = 
    names.strea().collect(Collectors.toCollection(ArrayList::new)); //Stream<String> -> ArrayList<String>

Map<String,Person> map = 
    personStream.collect(Collectors.toMap(p -> p.getRegId(), p -> p)); //Stream<Person> -> Map<String,Person>


    * Stream을 배열로 변환 - toArray()

Student[] stuNames = studentStream.toArray(Student[]::new);
Object[] stuNames = studentStream.toArray();


    ~ 통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy(), summarizingInt()

long count = stuStream.collect(counting()); //Collectors.counting() - import static~

long totalScore = stuStream.collect(summingint(Student::getTotalScore));

Optional<Student> topStudent = stuStream.collect(maxBy(Comparator.comparingInt(Student::getTotalScore)));

 
    ~ 문자열 결합 - joining()

String stuNames = stuStream.map(Student::getName).collect(joining());
String stuNames = stuStream.map(Student::getName).collect(joining(","));
String stuNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));
String stuNames = stuStream.collect(joining(","));


    ~ 리듀싱 - reducing()

IntStream intStream = new Random().ints(1,46).distinct().limit(6);

Optional<Integer> max = intStream.boxed().collect(reducing(Integer::max));

long sum = intStream.boxed().collect(reducing(0, (a,b) -> a + b));


    ~ 그룹화와 분할 - partitioningBy(): 2분할, groupingBy(): n분할, collectingAndThen()

Map<Boolean,List<Student>> stuBySex = 
        stuStream.collect(partitioningBy(Student::isFemale)); // 학생들을 성별로 분할
List<Student> femaleStudent = stuBySex.get(true); //Map에서 여학생 목록 얻기
List<Student> maleStudent = stuBySex.get(false); //Map에서 남학생 목록 얻기

- partitioningBy()

import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;
import static java.util.Comparator.*;

class Student2 {
	String name;
	boolean isFemale;
	int grade;
	int ban;
	int score;
	
	Student2(String name, boolean isFemale, int grade, int ban, int score) {
		this.name = name;
		this.isFemale = isFemale;
		this.grade = grade;
		this.ban = ban;
		this.score = score;
	}
	
	String getName() { return name; }
	boolean isFemale() { return isFemale; }
	int getGrade() { return grade; }
	int getBan() { return ban; }
	int getScore() { return score; }
	
	public String toString() {
		return String.format("[%s, %s, %d학년 %d반, %3d점]", 
				name, isFemale ? "여":"남", grade, ban, score);
	}
}

public class Ex20 {
	public static void main(String[] args) {
		Student2[] stuArr = {
				new Student2("나자바", true, 1, 1, 300),
				new Student2("김우주", false, 1, 1, 250),
				new Student2("김자바", true, 1, 1, 200),
				new Student2("이우주", false, 1, 2, 150),
				new Student2("남자바", true, 1, 2, 100),
				new Student2("안우주", false, 1, 2, 50),
				new Student2("황우주", false, 1, 3, 100),
				new Student2("강우주", false, 1, 3, 150),
				new Student2("이자바", true, 1, 3, 200),
				new Student2("나자바", true, 2, 1, 300),
				new Student2("김우주", false, 2, 1, 250),
				new Student2("김자바", true, 2, 1, 200),
				new Student2("이우주", false, 2, 2, 150),
				new Student2("남자바", true, 2, 2, 100),
				new Student2("안우주", false, 2, 2, 50),
				new Student2("황우주", false, 2, 3, 100),
				new Student2("강우주", false, 2, 3, 150),
				new Student2("이자바", true, 2, 3, 200),
		};
		
		System.out.println("1. 단순분할(성별로 분할)");
		Map<Boolean, List<Student2>> stuBySex = Stream.of(stuArr).collect(partitioningBy(Student2::isFemale));
		
		List<Student2> femaleStudent = stuBySex.get(true);
		List<Student2> maleStudent = stuBySex.get(false);
		
		for(Student2 s : femaleStudent) {System.out.println(s);}
		for(Student2 s : maleStudent) {System.out.println(s);}
		System.out.println();
		
		System.out.println("2. 단순분할 + 통계(성별 학생 수)");
		Map<Boolean, Long> stuNumBySex = Stream.of(stuArr)
				.collect(partitioningBy(Student2::isFemale, counting()));
		
		System.out.println("여학생 수 : " + stuNumBySex.get(true));
		System.out.println("남학생 수 : " + stuNumBySex.get(false));
		System.out.println();
		
		System.out.println("3. 단순분할 + 통계(성별 1등)");
		Map<Boolean, Optional<Student2>> topScoreBySex = Stream.of(stuArr)
				.collect(partitioningBy(Student2::isFemale, maxBy(comparingInt(Student2::getScore))));
		
		System.out.println("여학생 1등 : " + topScoreBySex.get(true));
		System.out.println("남학생 1등 : " + topScoreBySex.get(false));
		
		Map<Boolean, Student2>  topScoreBySex2 = Stream.of(stuArr)
				.collect(partitioningBy(Student2::isFemale, 
					collectingAndThen(maxBy(comparingInt(Student2::getScore)), Optional::get
					)
				));
		
		System.out.println("여학생 1등 : " + topScoreBySex2.get(true));
		System.out.println("남학생 1등 : " + topScoreBySex2.get(false));
		System.out.println();
		
		System.out.println("4. 다중분할()성별 불합격자, 100점 이하");
		
		Map<Boolean, Map<Boolean, List<Student2>>> failedStuBySex =
				Stream.of(stuArr).collect(partitioningBy(Student2::isFemale,
						partitioningBy(s -> s.getScore() <= 100))
				);
		
		List<Student2> failedFemaleStu = failedStuBySex.get(true).get(true);
		List<Student2> failedMaleStu = failedStuBySex.get(false).get(false);
		
		for(Student2 s : failedFemaleStu) {System.out.println(s);}
		for(Student2 s : failedMaleStu) {System.out.println(s);}
				
	}
}

- groupingBy()

import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;
import static java.util.Comparator.*;

class Student3 {
	String name;
	boolean isFemale;
	int grade;
	int ban;
	int score;
	
	Student3(String name, boolean isFemale, int grade, int ban, int score) {
		this.name = name;
		this.isFemale = isFemale;
		this.grade = grade;
		this.ban = ban;
		this.score = score;
	}
	
	String getName() { return name; }
	boolean isFemale() { return isFemale; }
	int getGrade() { return grade; }
	int getBan() { return ban; }
	int getScore() { return score; }
	
	public String toString() {
		return String.format("[%s, %s, %d학년 %d반, %3d점]", 
				name, isFemale ? "여":"남", grade, ban, score);
	}
	
	enum Level {
		HIGH, MID, LOW
	}
}

public class Ex21 {
	public static void main(String[] args) {
		
		Student3[] stuArr = {
				new Student3("나자바", true, 1, 1, 300),
				new Student3("김우주", false, 1, 1, 250),
				new Student3("김자바", true, 1, 1, 200),
				new Student3("이우주", false, 1, 2, 150),
				new Student3("남자바", true, 1, 2, 100),
				new Student3("안우주", false, 1, 2, 50),
				new Student3("황우주", false, 1, 3, 100),
				new Student3("강우주", false, 1, 3, 150),
				new Student3("이자바", true, 1, 3, 200),
				new Student3("나자바", true, 2, 1, 300),
				new Student3("김우주", false, 2, 1, 250),
				new Student3("김자바", true, 2, 1, 200),
				new Student3("이우주", false, 2, 2, 150),
				new Student3("남자바", true, 2, 2, 100),
				new Student3("안우주", false, 2, 2, 50),
				new Student3("황우주", false, 2, 3, 100),
				new Student3("강우주", false, 2, 3, 150),
				new Student3("이자바", true, 2, 3, 200),
		};
		
		System.out.println("1. 단순그룹화(반별로)");
		Map<Integer, List<Student3>> stuByBan = Stream.of(stuArr).collect(groupingBy(Student3::getBan));
		
		for(List<Student3> ban : stuByBan.values()) {
			for(Student3 s : ban) {
				System.out.println(s);
			}
		}
		System.out.println();
		
		System.out.println("2. 단순그룹화(성적별로)");
		Map<Student3.Level, List<Student3>> stuByLevel = Stream.of(stuArr)
				.collect(groupingBy (s -> {
					if(s.getScore() >= 200) { return Student3.Level.HIGH; }
					else if(s.getScore() >= 100) { return Student3.Level.MID; }
					else { return Student3.Level.LOW; }
				}));
		
		TreeSet<Student3.Level> keySet = new TreeSet<>(stuByLevel.keySet());
		
		for(Student3.Level key : keySet) {
			System.out.println("[" + key + "]");
			
			for(Student3 s : stuByLevel.get(key)) {
				System.out.println(s);
			}
			
			System.out.println();
		}
		
		System.out.println("3. 단순그룹화 + 통계(성적별 학생수)");
		Map<Student3.Level, Long> stuCntByLevel = Stream.of(stuArr)
				.collect(groupingBy(s -> {
					if(s.getScore() >= 200) { return Student3.Level.HIGH; }
					else if(s.getScore() >= 100) { return Student3.Level.MID; }
					else { return Student3.Level.LOW; }
				}, counting()));
		
		for(Student3.Level key : stuCntByLevel.keySet()) {
			System.out.printf("[%s] - %d명\n", key, stuCntByLevel.get(key));
		}
		System.out.println();
		
		System.out.println("4. 다중그룹화(학년별, 반별)");
		Map<Integer, Map<Integer, List<Student3>>> stuByGradeBan = Stream.of(stuArr)
				.collect(groupingBy(Student3::getGrade, groupingBy(Student3::getBan)));
		
		for(Map<Integer, List<Student3>> grade : stuByGradeBan.values()) {
			for(List<Student3> ban : grade.values()) {
				for(Student3 s : ban) {
					System.out.println(s);
				}
				System.out.println();
			}
		}
		
		System.out.println("5. 다중그룹화 + 통계(학년별, 반별 1등)");
		Map<Integer, Map<Integer, Student3>> topStuByGradeBan = Stream.of(stuArr)
				.collect(groupingBy(Student3::getGrade,
						groupingBy(Student3::getBan,
								collectingAndThen(
										maxBy(comparingInt(Student3::getScore))
										, Optional::get
								)
						)
				));
		
		for(Map<Integer, Student3> ban : topStuByGradeBan.values()) {
			for(Student3 s : ban.values()) {
				System.out.println(s);
			}
		}
		System.out.println();
		
		System.out.println("6. 다중그룹화 + 통계(학년별, 반별 성적 그룹)");
		Map<String, Set<Student3.Level>> stuByScoreGroup = Stream.of(stuArr)
				.collect(groupingBy(s -> s.getGrade() + "-" + s.getBan(),
						mapping(s -> {
							if(s.getScore() >= 200) { return Student3.Level.HIGH; } 
							else if(s.getScore() >= 100) { return Student3.Level.MID; }
							else { return Student3.Level.LOW; }
						}, toSet())
				));
		
		Set<String> keySet2 = stuByScoreGroup.keySet();
		
		for(String key : keySet2) {
			System.out.println("[" + key + "]" + stuByScoreGroup.get(key));
		}
	
	}
}

// 스트림의 변환

from to 메서드
1. 스트림 -> 기본형 스트림
Stream<T> IntStream
LongStream
DoubleStream
mapToInt(ToIntFunction<T> mapper)
mapToLong(ToLongFunction<T> mapper)
mapToDouble(ToDoubleFunction<T> mapper)
2. 기본형 스트림 -> 스트림
IntStream
LongStream
DoubleStream
Stream<Integer>
Stream<Long>
Stream<Double>
boxed()
Stream<U> mapToObj(DoubleFunction<U> mapper)
3. 기본형 스트림 -> 기본형 스트림
IntStream
LongStream
DoubleStream
LongStream
DoubleStream
asLongStream()
asDoulbeStream()
4. 스트림 -> 부분 스트림
Stream<T>
IntStream
Stream<T>
IntStream
skip(long n)
limit(long maxSize)
5. 두 개의 스트림 -> 스트림
Stream<T>, Stream<T> Stream<T> concat(Stream<T> a, Stream<T> b)
IntStream, IntStream IntStream concat(IntStream a, IntStreamb)
LongStream, LongStream LongStream concat(LongStream a, LongStream b)
DoubleStream, DoubleStream DoubleStream concat(DoubleStream a, DoubleStream b)
6. 스트림의 스트림 -> 스트림
Stream<Stream<T>> Stream<T> flatMap(Fuction mapper)
Stream<IntStream> IntStream flatMapToInt(Fuction mapper)
Stream<LongStream> LongStream flatMapToLong(Fuction mapper)
Stream<DoubleStream> DoubleStream flatMapToDouble(Fuction mapper)
7. 스트림 <-> 병렬 스트림
Stream<T>
IntStream
LongStream
DoubleStream
Stream<T>
IntStream
LongStream
DoubleStream
parallel() //스트림 -> 병렬 스트림
sequential() //병렬 스트림 -> 스트림
8. 스트림 -> 컬렉션
Stream<T>
IntStream
LongStream
DoubleStream
Collection<T> collect(Collectors.toCollection(Supplier factory))
List<T> collect(Collectors.toList())
Set<T> collect(Collectors.toSet())
9. 컬렉션 -> 스트림
Collection<T>, List<T>, Set<T> Stream<T> stream()
10. 스트림 -> Map
Stream<T>
IntStream
LongStream
DoubleStream
Map(K,V) collect(Collectors.toMap(Fuction key, Function value))
collect(Collectors.toMap(Function k, Function v,
BinaryOperator))
collect(Collectors.toMap(Fuction k, Function v,
BinaryOperator merge, Supplier mapSupplier))
11. 스트림 -> 배열
Stream<T> Object[] toArray()
T[] toArray(IntFunction<A[]> generator)
IntStream
LongStream
DoubleStream
int[]
long[]
double[]
toArray()

- boxed()

package com.sorrel012.functionalprogramming;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class IntStreamOperation {

	public static void main(String[] args) {

		List<Integer> list = IntStream.range(1, 10).map(e -> e * e).boxed()
													.collect(Collectors.toList());
		System.out.println(list);
		
	} //main
	
}

'자바(JAVA)' 카테고리의 다른 글

[자바(Java)] 스트림 활용  (0) 2022.12.31
[자바(Java)] 문자열 <-> 배열 변환  (0) 2022.12.31
[자바(Java)] 람다식  (0) 2022.12.20
[자바(Java)] 쓰레드(Thread)  (0) 2022.12.18
[자바(Java)] 애너테이션  (0) 2022.12.17