자바의정석 ch14

soso·2023년 2월 27일
0
post-thumbnail

Chapter14 람다와 스트림

람다식(Lambda Expression)

▶함수(메서드)를 간간한 '식(expression)'으로 표현하는 방법

int max(int a, int b) {
	return a > b ? a : b ; 
}
			↓↓↓↓
            
(a, b) -> a > b ? a : b            

▶익명 함수(이름없는 함수, anonymous function)

int max(int a, int b) {
	return a > b ? a : b ; 
}
			↓↓↓↓
int max(int a, int b) -> {
	return a > b ? a : b ; 
}
            

▶함수와 메서드의 차이
-근본적으로 동일. 함수는 일반적 용어, 메서드는 객체지향개념 용어
-함수는 클래스에 독립적, 메서드는 클래스에 종속적

람다식 작성하기

  1. 메서드의 이름과 반한타입을 제거하고 '->'를 블록{} 앞에 추가한다.
  2. 반환값이 있는경우, 식이나 값만 적고 return문 생략 가능(끝에 ';'안 붙임)
  3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)###

람다식 작성하기 -주의사항

  1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
(a) -> a * a
(int a) -> a * a
	↓↓↓↓
a -> a * a	// OK
int a -> a * a   //에러     
  1. 블록 안의 문장이 한나뿐 일 때, 괄호{}생략가능(끝에';'안 붙임)
(int i) -> {
  System.out.println(i); 
}
	↓↓↓↓
(int i) -> System.out.println(i)

단, 하나뿐인 문장이 return문이면 괄호{} 생략 불가

(int a, int b) -> { return a > b ? a : b;}	// OK
	↓↓↓↓
(int a, int b) -> return a > b ? a : b	// 에러

람다식의 예

람다식은 익명함수? 익명 객체!

▶람다식은 익명 함수가 아니라 익명 객체이다.

(a, b) ->a > b ? a : b
	↓↓↓↓
new Object() {
	int max(int a, int b) {
    	return a > b ? a : b;
	}
}    

▶람다식(익명객체)를 다루기 위한 참조변수가 필요.
참조형이니까 클래스 또는 인터페이스가 가능
람다식과 동등한 메소드가 정의 되어있어야 한다.
참조변수로 익명객체(람다식)의 메서드를 호출할수 있기 때문이다.

함수형 인터페이스(Functional Interface)

▶단 하나의 추상 메서드만 선언된 메서드이다.

interface MyFunction{
	public abstract int max(int a, int b);
			 ↓↓↓↓
MyFunction f = new MyFunction() {
	public  int max(int a, int b){
    	return a > b ? a : b;
	}
}        
int value = f.max(3,5); // OK. MyFunction에 max가 있음

▶함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있다.
(단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야 한다.)

MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3,5); // 실제로는 람다식(익명 함수)이 호출됨

함수형 인터페이스에는 오직 하나의 추상 메서드만 정의 되어있어야 한다는 제약이 있다.람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문이다.

함수형 인터페이스 타입의 매개변수, 반환 타입

-함수형 인터페이스 타입의 매개변수
-함수형 인터페이스 타입의 반환타입

java.util.function패키지

▶자주 사용되는 다양한 함수형 인터페이스를 제공한다.

매개변수가 두 개인 함수형 인터페이스

매개변수의 개수가 2개인 함수형 인터페이스는 이름 앞에 접두사 Bi가 붙는다.

UnaryOperator와 BinaryOperator

Function의 또 다른 변형으로 UnaryOperator와 BinaryOperator가 있는데, 매개변수의 타 입과 반환타입의 타입이 모두 일치한다는 점만 제외하고는 Function과 같다.

Predicate의 결합

▶and(), or(), negate()로 두 Predicated를 하나로 결합(default메서드)

Predicated<Integer> p = i -> i < 100;
Predicated<Integer> q = i -> i < 200;
Predicated<Integer> r = i -> i%2 == 0;
Predicated<Integer> notP = p.negate();	// i >= 100

 // 100 <= i && i <200 || i%2==0
Predicated<Integer> all = notP.and(q).or(r);
System.out.println(all.test(150));	// true

▶등가비교를 위한 Predicate의 작성에는 isEqual()를 사용 (static메서드)

Predicate<String› p = Predicate.isEqual(str1);
boolean result = p.test(str2); //str1과 str2가 같은지 비교하여 결과를 반환

컬렉션 프레임워크와 함수형 인터페이스

메서드 참조(method reference)

▶하나의 메서드만 호출하는 람다식은 '메서드 참조'로 더 간단히 할 수 있다.

▶static메서드 참조

Integer method (sitrans s) { //그저 Integer.parseInt(string s) 만 호를 
	return Integer.parseInt(s);
} 
			  ↓↓↓↓
Function<String, Integer> f = (String s) -> Integer.parseInt(S);   
  			  ↓↓↓↓
Function<string, Integer> f = Integer: : parseInt; // 메서드 참조

생성자의 메서드 참조

▶생성자와 메서드 참조

Supplier<MyClass> s = () -> new MyClass(); // 람다식
Supplier<MyClass> s = MyClass:: new; //메서드 참조

▶배열과 메서드 참조

Function‹Integer, int[]> f = x -› new int [x]; //람다식
Function<Integer, int[]> f2 = int[]::new; //메서드 참조

스트림(stream)

스트림이 제공하는 기능- 중간 연산과 최종 연산

  • 중간연산 -연산결과가 스트림인 연산. 반복적으로 적용가능
  • 최종연산 -연산결과가 스트림이 아닌 연산. 단 한번만 적용가능(스트림 요소를 소모)

스트림의 특징

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

List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream(); // 컬렉션
Stream<String> strStream = Stream.of(new String[]{"a","b","c"}); // 배열
Stream<Integer> evenStream = Stream.iterate(0, n -> n+2); // 0,2,4,6,...
Stream<Double> randomStream = Stream.generate(Math::random); // 람다식

▶스트림은 테이터 소스로부터 데이터를 읽기만 할뿐 변셩하지 않는다.

List<Integer> list = Arrays.asList(3,1,5,4,2);
List<Integer> sortedList = list.stream().sorted() // list를 정렬해서
						.collect(Collectors.toList()); // 새로운 List에 저장
System.out.println(list); // [3,1,5,4,2]
System.out.println(sortedList); // [1,2,3,4,5]

▶스트림은 Iterator처럼 일회용이다.(필욜하면 다시 스트림을 생성해야 함)

strStream.forEach(System.out.println);	// 모든 요소를 화면에 출력(최종연산)
int numOfStr = strStream.count();	// 에러. 스트림이 이미 닫혔음

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

IntStream intStream = new Random().ints(1,46);	// 1~45범위의 무한 스트림
intStream.distinct().limt(6).sorted() // 중간연산
		 .forEach(1->System.out.println)(1+",")); //최종연산

▶스트림은 작업을 내부 반복적으로 처리한다.

for(String str : strList)
	System.out.println(str);
    ↓ ↓ ↓ ↓
stream.forEach(System.out::println);    

▶스트림의 작업을 병렬로 처리 -병렬스트림

Stream<String> strStream = Stream.of("dd","aaa","CC","cc","b");
int sum = strStream.parallel() // 병렬 스트림으로 전환(속성만 변경)
				.mapToInt(s -> s.lenght()).sum(); // 모든 문자열의 길이의 합

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

스트림 만들기 -컬렉션

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

Stream〈E〉stream() // Collection인터페이스의 메서드

forEach()는 지정된 작업을 스트림의 모든 요소에 대해 수행한다.
한 가지 주의할 점은 forEach()가 스트림의 요소를 소모하면서 작업을 수행하므로 같은 스트립에 forEach()를 두 번 호출할 수 없다.

그래서 스트림의 요소를 한번 더 출력하려면 스트림을 새로 생성해야 한다.

forEach()에 의해 스트림의 요소가 소모되는 것이지, 소스의 요소가 소모되는것이 아니기 때문에 같은 소스로부터 다시 스트림을 생성할 수 있다.

스트림 만들기 -배열

배열을 소스로 하는 스트림을 생성하는 메서드는 Stream과 Arrays에 static메서드로 정의되어 있다.

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)

문자열스트림

Stream<String> strstream=stream.of("a", "b", "c"); // 가변 인자
Stream<String> strStream=Stream.of(new String[]{"a", "b", "c"});
Stream<String> strStream=Arrays.stream(new String[]{"a", "b", "c"});
Stream<String> strStream=Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);

int, long, double과 같은 기본형 배열을 소스로 하는 스트림을 생성하는 메서드도 있다.

IntStream IntStream.of(int... values) // Stream이 아니라 IntStream
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)

이 외에도 long과 double타입의 배열로부터 LongStream과 Doublestream을 반환하는 메서드들이 있다.

스트림 만들기 -임의의 수

난수를 생성하는데 사용하는 Random클래스에는 아래와 같은 인스턴스 메서드들이 포함되 어 있다. 이 메서드들은 해당 타입의 난수들로 이루어진 스트림을 반환한다.

IntStream	ints()
LongStream	longs()
DoubleStream	doubles() 

이 메서드들이 반환하는 스트림은 크기가 정해지지 않은 무한 스트림(infinite stream)'이므로 limit()도 같이 사용해서 스트림의 크기를 제한해 주어야 한다.
limit()은 스트림의 개수를 지정하는데 사용되며, 무한 스트림을 유한 스트림으로 만들어 준다.

Intstream intstream = new Random(). ints(); // 무한 스트림
intstream.limit(5).forEach(system.out::println); // 5개의 요소만 출력한다.

아래의 메서드들은 매개변수로 스트림의 크기를 지정해서 '유한 스트림'을 생성해서 반환하므로 limit()을 사용하지 않아도 된다.

IntStream	ints(long streamSize)
LongStream	longs(long streamSize)
DoubleStream	doubles(long streamsize)
Intstream intstream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환

위 메서드들에 의해 생성된 스트림의 난수는 아래의 범위를 갖는다.

Integer MIN_VALUE <= ints() <= Integer.MAX_VALUE
Long. MIN_VALUE <=  longs() <= Long. MAX_VALUE
			0.0 <= doubles() < 1.0

스트림 만들기 -특정 범위의 정수

IntStream과 LongStream은 다음과 같이 지정된 범위의 연속된 정수를 스트림으로 생성해서 반환하는 range()와 rangeClosed()를 가지고 있다.

IntStream IntStream.range(int begin, int end)
IntStream IntStream. rangeClosed(int begin, int end)

range()의 경우 경계의 끝인 end가 범위에 포함되지 않고,rangeClosed()의 경우는 포함된다.

IntStream intStream = IntStream.range(1, 5);	//1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5);	//1,2,3,4,5  

int보다 큰 범위의 스트림을 생성하려면 LongStream에 있는 동일한 이름의 메서드를 사용 하면 된다.
지정된 범위(begin~end)의 난수를 발생시키는 스트림을 얻는 메서드는 아래와 같다. end는 범위에 포함되지 않는다.

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) 

스트림 만들기 -람다식 iterate(), generate()

▶람다식을 소스로 하는 스트림 생성하기

static〈T〉Stream〈T〉iterate(T seed, UnaryOOperator〈T〉 f) // 이전 요소에 종속적
static〈T〉Stream〈T〉generate(Supplier〈T〉s) // 이전 요소에 독립적

▶iterate()는 이전 이전요소를 seed로 해서 다음 요소를 계산한다.
▶generate()는 seed를 사용하지 않는다

스트림 만들기 -파일과 빈 스트림

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

Stram〈Path〉 Files.list(Path dir) //Path는 파일 또는 디렉토리

빈 스트림
요소가 하나도 없는 비어있는 스트림을 생성할 수도 있다. 스트림에 연산을 수행한 결과가 하 나도 없을 때, null보다 빈 스트림을 반환하는 것이 낫다.

stream emptystream = stream. empty(); // empty()는 빈 스트림을 생성해서 반환한다.
long count = emptyStream. count (); //count의 값은 0  

count()는 스트림 요소의 개수를 반환하며, 위의 문장에서 변수 count의 값은 0이 된다.

스트림의 연산

중간연산 -연산결과가 스트림인 연산. 반복적으로 적용가능
최종연산 -연산결과가 스트림이 아닌 연산. 단 한번만 적용가능(스트림 요소를 소모)

스트림의 연산 -중간연산

중간연산은 map()과 flatMap()가 핵심이다.

스트림의 연산 -최종연산

최종연산은 reduce()와 collect()가 핵심이다.

스트림의 중간연산 -skip(), limit()

skip(), limit()은 스트림의 일부를 잘라낼때 사용한다.

Stream〈T〉 skip(long n) // 앞에서부터 n개 건너뛰기
Stream〈T〉 limit(long maxSize) // maxSize 이후의 요소는 잘라냄

스트림의 중간연산 -filter(), distinct()

distinct() : 스트림에서 중복 제거
filter : 주어진 조건(Predicate)에 맞지 않는 요소를 걸러낸다.

Stream〈T〉 filter(Predicate<? super T> predicate) //
Stream〈T〉 distinct()

스트림의 중간연산 -sorted()

sorted() : 스트림을 정렬할 때 사용

Stream〈T〉 sorted() // 스트림 요소의 기본 정렬(Comparable)로 정렬
Stream〈T〉 sorted(Comparator<? super T> comparator ) //지정된 Comparator로 정렬

스트림의 중간연산 -Comparator의 메서드

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

comparing(Function 〈T, U〉 keyExtractor)
comparing(Function 〈T, U〉 keyExtractor, Comparator 〈U〉 keyComparator)

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

thenComparing (Comparator 〈T〉 other)
thenComparing(Function 〈T, U〉 keyExtractor)
thenComparing(Function 〈T, U〉 keyExtractor, Comparator 〈U〉 keyComp)

스트림의 중간연산 -map()

map() : 스트림의 요소 변환하기

Stream 〈R〉 map (Function〈? super T, ? extends R〉 mapper) // Stream 〈T〉 → Stream 〈R〉

스트림의 중간연산 -peek()

peek(): 스트림의 요소를 소비하지 않고 엿보기

Stream 〈T〉 peek(Consumer〈? super T〉 action) // 중간 연산(스트림을 소비X)
void forEach(Consumer〈? super T〉 action) // 최종 연산(스트림을 소비O)

스트림의 중간연산 -flatMap()

flatMap() : 스트림을 스트림으로 변환

Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc", "def", "ghi" },
new String[]{"ABC", "GHI", "JKLMN" }
);

Optional〈T〉

T타입의 객체'를 감싸는 래퍼 클래스이다.
그래서 Optional타입의 객체에는 모든 타입의 객체를 담을 수 있다.

public final class Optional〈T〉 {
private final T vlaue; // T타입의 참조변수
...
}

최종 연산의 결과를 그냥 반환하는 게 아니라 Optional객체에 담아서 반환을 하면, 반환된 결 과가 null인지 매번 if문으로 체크하는 대신 Optional에 정의된 메서드를 통해서 간단히 처리 할 수 있다.

Optional〈T〉 객체 생성하기

Optional객체를 생성할 때는 of() 또는 ofNullable()을 사용한다.

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

만일 참조변수의 값이 nul1일 가능성이 있으면, of()대신 OfNullable()을 사용해야한다.
of()는 매개변수의 값이 nul이면 Null Pointer Exception일 발생하기 때문이다.

Optional<String> optVal = Optional.of(null); // NullPointerExceptionary
Optional<String> optVal = Optional.ofNullable(null); // OK

Optional〈T〉타입의 참조변수를 기본값으로 초기화할 때는 empty()를 사용한다. null로 초기화하는 것이 가능하지만, empty()로 초기화 하는 것이 바람직하다.

Optional〈T〉의 객체의 값 가져오기

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

Optional〈String〉ptVal = Optional.of ("abc");
String strl = optVal.get(); // optval에 저장된 값을 반환. nall이면 예외발생
String str2 = optVal.orElse("") ; //optval에 저장된 값이 nu11일 때는, ""를 반환
String str3 = optVal.orElseGet(String::new); //람다식 사용가능 () -> new string()
String str4 = optVal.orElseThrow(NullPointerException:: new); // 널이면 예외발생

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

if(Optional.ofNullable(str).isPresent()) { // if(str != null) {
		System.out.println(str);
}

OptionalInt, OptionalLong, OptionalDouble

▶기본형 값을 감싸는 래퍼클래스
public final class OptionalInt {
...
private final boolean isPresent; // 값이 저장되어 있으면 true
private finalint value; // int타입의 변수

▶Optional의 값 가져오기 -int getAsInt()

▶빈 Optional객체와의 비교
OptionalInt opt = OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt2 = OptionalInt.empty(); // OptionalInt에 0을 저장

저장된 값이 없는 것과 0이 저장된 것은 isPresent 인스턴스 변수로 구분이 가능하다.
System.out.println(opt.isPresent()); // true
System.out.println(opt2.isPresent()); // false
System.out.println(opt.equals(opt2)); // false

스트림의 최종연산 -forEach()

▶스트림의 모든 요소에 지정된 작업을 수행 - forEach(), forEachOrdered()

void forEach(Consumer〈? super T〉 action) // 병렬스트림인 경우 순서가 보장되지 않음
void forEachOrdered(Consumer〈? super T〉 action) // 병렬스트림인 경우에도 순서가 보장됨

최종 연산은 스트림의 요소를 소모해서 결과를 만들어낸다.
그래서 최중 연산후에 스트립트는 닫히게 되고 더 이상 사용할 수 없다.
최종 연산의 결과는 스트림 요소의 합과 같은 단일 값이 거나, 스트림의 요소가 담긴 배열 또는 컬렉션일 수 있다.

forEach

forEach()는 peek()와 달리 스트림의 요소를 소모하는 최종연산이다.
반환 타입이 void이므로 스트림의 요소를 출력하는 용도로 많이 사용된다.

스트림의 최종연산 -조건검사

스트림의 요소에 대해 지정된 조건에 모든 요소가 일치하는 지, 일부가 일치하는지 아니면 어 떤 요소도 일치하지 않는지 확인하는데 사용할 수 있는 메서드들이다

▶조건검사 -allMatch(), anyMatch(), noneMatch()

boolean allMatch (Predicate〈? super T〉 predicate) // 모든 요소가 조건을 만족(일치)시키면 true
boolean anyMatch (Predicate〈? super T〉 predicate)// 한 요소라도 조건을 만족(일치)시키면 true
boolean noneMatch(Predicate〈? super T〉 predicate) // 모든 요소가 조건을 만족(일치)시키지 않으면 true

▶조건에 일치하는 요소 찾기 -findFirst(), findAny()

Optional〈T〉 findFirst() // 첫 번째 요소를 반환. 순차 스트림에 사용
Optional〈T〉findAny() // 조건에 일치하는 요소를 하나 반환. 병렬 스트림에 사용

스트림의 최종연산 -reduce()

스트림의 요소를 하나씩 줄여가며 누적연산을 수행한다.

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

-identity : 초기값
-accumulator : 이전 연산결과와 스트림의 요소에 수행할 연산
-combiner : 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림)

collect()와 Collectors

collect()는 스트림의 요소를 수집하는 최종 연산으로 리듀싱(reducing)와 유사하다.
collect()가 스트림의 요소를 어떻게 수집할지 방법을 정의한것이 컬렉터(collector)이다.

collect : 스트림의 최종연산, 매개변수로 컬렉터를 필요로 한다.
Collector : 인터페이스, 컬렉터는 이 인터페이스를 구현해야한다.
Collectors : 클래스, ststic메서드로 미리 작성된 컬렉터를 제공한다.

▶collect()는 Collector를 매개변수로 하는 스트림의 최종연산

Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) // 잘 안쓰임

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

public interface Collector<T, A, R> { // T(요소)를 A에 누적한 다음, 결과를 R로 변환해서 반환
Supplier<A>			 supplier(); 		// StringBuilder::new 누적할 곳
BiConsumer<A, T>	 accumulator();		// (sb, s) -> sb.append(s) 누적방법
Binary0perator<A>	 combiner();		// (sb1, sb2) -> sb1. append (sb2) 결합방법〔병렬)
Function<A, R> 		 finisher();		// sb -> sb. tostring () 최종변환
Set<Characteristics> characteristics(); // 컬렉터의 특성이 담긴 set을 반환

▶Collectors클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스) 제공

  • 변환 : mapping(), toList(), toSet(), toMap(), toCollection(), ...
  • 통계 : counting(), summingInt(), averagingInt(), maxBy(), minBy(), summarizingInt(), ...
  • 문자열 결합 : joining()
  • 리듀싱 : reducing()
  • 그룹화와 분할 : groupingBy(), partitioningBy(), collectingAndThen()

스트림을 컬렉션, 배열로 변환

▶스트림을 컬렉션으로 변환 -oList(), 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.stream()
	.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>

▶스트림을 배열로 변환-toArray()

Student[] stuNames = studentStream.toArray(Student[]::new); // OK
Student[] stuNames = studentStream.toArray(); // 에러
Object[] stuNames = studentStream.toArray(); // OK.

스트림의 통계 -counting(), summingInt()

▶스트림의 통계정보 제공 -counting(), summingInt(), maxBy(), minBy()

long count = stuStream.count();
long count = stuStream.collect(counting()); // Collectors.counting()

long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum()
long totalScore = stuStream.collect(summingInt(Student::getTotalScore));

OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();
Optional<student> topStudent = stuStream
						.max(Comparator.comparingInt(Student::getTotalScore));
Optional<student> topStudent = stuStream
			.collect(maxBy(Comparator.comparingInt(Student::getTotalScore)));

스트림을 리듀싱 -reducing()

Collectors.reducing()에는 아래와 같이 3가지 종류가 있다.
세 번째 메서드만 제외하고 reduce()와 같다.
세 번째 것은 map()과 reduce()를 하나로 합처 놓은 것 뿐이다.

Collector reducing (Binaryoperator<T> op)
Collector reducing(T identity, Binaryoperator‹T> op)
Collector reducing(U identity, Function<T,U> mapper, BinaryOperator<U> op)

스트림을 문자열로 결합 -joining()

문자열 스트림의 모든 요소를 하나의 문자열로 연결해서 반환한다.
구분자, 접두사, 접미사도 지정가능하다.
스트림의 요소가 String이나 StringBuffer처럼CharSequence의 자손인 경우에만 결합이 가능하므로 스트림의 요소가 문자열이 아닌 경우에는 먼저 map()을 이용해서 스트림의 요소를 문자열로 변환해야 한다.

String studentNames = stuStream.map(Student::getName).collect (joining());
String studentNames = stuStream.map(Student::getName).collect (joining(",")); // 구분자
String studentNames = stuStream.map(Student::getName).collect (joining(",", "[", "]"));
String studentInfo = stuStream.collect(joining(",")); // student의 toString()으로 결합

스트림의 그룹화와 분할

그룹화는 스트림의 요소를 특정 기준으로 그룹화하는 것을 의미하고, 분할은 스트림의 요 소를 두 가지, 지정된 조건에 일치하는 그룹과 일치하지 않는 그룹으로의 분할을 의미한다.

groupingBy()는 스트림의 요소를 Function으로,
partitioningBy()Predicate로 분류한다.

스트림의 분할 -partitiongBy()

▶partitiongBy()는 스트림을 2분할한다.

Collector partitiongBy(Predicate predicate)
Collector partitiongBy(Predicate predicate, Collector downstream)

스트림의 그룹화 -groupingBy()

▶groupingBy()는 스트림을 n분할한다.

Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
```**
### 스트림의 변환
![](https://velog.velcdn.com/images/soso/post/f38c0238-ed0e-43d3-87f0-b0e4735f8796/image.jpg)![](https://velog.velcdn.com/images/soso/post/25b4d523-0c05-4161-bc92-df546c01cb24/image.jpg)


profile
오늘의 기록

0개의 댓글