1
2
3
4
5
6
7
8
9
10
11
12
String [] strAr = { "abc" , "cde" , "awef" , "ccd" };
Stream < String > stringStream = Arrays . stream ( strAr )
. sorted ( Comparator . comparingInt ( String: : length ). reversed ())
. peek ( System . out :: println )
. map ( String: : toUpperCase )
. map ( x ->{
System . out . println ( x );
return x ;
})
. filter ( x -> x . contains ( "C" ));
List < String > ls = stringStream . collect ( Collectors . toList ());
System . out . println ( "ls = " + ls );
실행 결과
1
2
3
4
5
6
7
8
9
awef
AWEF
abc
ABC
cde
CDE
ccd
CCD
ls = [ABC, CDE, CCD]
Stream<T> sorted([Comparator<? super T> comparator]) : 정렬
Comparator.reverseOrder() : 내림차순
Comparator.comparingInt(String::length)) : 글자수 기준 오름차순
내림차순으로 하려면 reversed()를 체이닝하면 됨
위 결과에서는 단순히 글자수로 정렬했기 때문에 reversed()를 사용해도 이전의 순서가 유지되는 것처럼 보임
정렬 기준이 복잡해지면 stable하게 바뀌는지는 모름
람다로도 가능 : (s1, s2) -> s2.length() - s1.length()
연산 결과가 true인 경우 두 원소의 순서 변경
Stream<T> peek(Consumer<? super T> action) : stream 원소 별로 action 연산 수행
consumer가 아닌 연산이 들어와서 연산 결과가 리턴되어도 원소에 영향을 미치지 않음
<R> Stream<R> map(Function<? super T, ? extends R> mapper) : stream에 mapper 매핑
원소 값이 리턴되는 연산 결과가 원소에 대입됨
<R> Stream<R> 타입이 뭔지는 모르겠음
flatMap() : 리턴 값에서 중첩 구조를 한 단계 제거하고 붙여줌
객체를 flattening 할 때도 사용 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// obj1 : String s1, s2, s3을 필드로 가짐
obj1 [] objs = new obj1 [ 3 ];
objs [ 0 ] = new obj1 ( "a1" , "b1" , "c1" );
objs [ 1 ] = new obj1 ( "a2" , "b2" , "c2" );
objs [ 2 ] = new obj1 ( "a3" , "b3" , "c3" );
List < String > ls = Arrays . stream ( objs )
. map ( o -> Stream . of ( o . getS1 (),
o . getS2 (),
o . getS3 ())
. reduce ( "" , ( a , b ) -> a + b ))
. collect ( Collectors . toList ());
String as1 = Arrays . stream ( objs )
. map ( o -> Stream . of ( o . getS1 (),
o . getS2 (),
o . getS3 ())
. reduce ( "" , ( a , b ) -> a + b ))
. reduce ( "" , ( a , b ) -> a + b );
String as2 = Arrays . stream ( objs )
. flatMap ( o -> Stream . of (
o . getS1 (),
o . getS2 (),
o . getS3 ()))
. reduce ( "" , ( a , b ) -> a + b );
실행 결과
1
2
3
ls = [ a1b1c1 , a2b2c2 , a3b3c3 ]
as1 = a1b1c1a2b2c2a3b3c3
as2 = a1b1c1a2b2c2a3b3c3
위 코드에서는 필드가 String이라 동일한 결과를 만들어낼 수 있지만, 객체 별로 필드 개수가 다른 상황에서 전체 필드들의 평균을 구하는 등의 상황에서는 .average()를 .reduce() 대신 사용하면 모든 필드들의 평균이 제대로 구해지지 않기 때문에 flatMapToInt()를 사용해서 flattened stream으로 만든 다음 .average()를 사용해야 함
Stream<T> filter(Predicate<? super T> predicate) : predicate 연산의 결과가 true인 원소만 보존
Terminal operation
count(), min(), max(), sum(), average()
stream이 비어 있는 경우를 대비하여 min(), max(), average()는 OptionalInt 타입으로 리턴함
ifPresent(System.out::println) 등의 메소드로 chaining 가능
reduce()
1
2
3
4
5
6
7
Optional < T > reduce ( BinaryOperator < T > accumulator );
T reduce ( T identity , BinaryOperator < T > accumulator );
< U > U reduce ( U identity ,
BiFunction < U , ? suer T , U > accumulator ,
BinaryOperator < U > combiner );
accumulator : 각 원소들을 누적하는 연산
identity : 초기값
combiner : 병렬 처리할 때 합치는 연산
forEach()
intermediate operation 중 peek()와 유사함
<R, A> R collect(Collector<? super T, A, R> collector)
Collectors.toList() : 리스트로 반환
Collectors.joining() : 결과를 하나의 String으로 반환(delimiter, prefix, suffix 파라미터로 넘길 수 있음)
Collectors.averageInt(), Collectors.summingInt(), Collectors.summarizingInt()
summarizingInt() : IntSummaryStatistics 타입 반환(count, sum, min, average, max를 담은 객체)
Collectors.groupingBy() : functional interface Function을 파라미터로 받아 Map 타입으로 반환
key : Function의 결과값
value : 피연산 객체들 리스트
Collectors.partitioningBy() : functional interface Predicate를 파라미터로 받아 Map 타입으로 반환(groupingBy()와 동일)
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) : downstream을 수행한 다음 finisher를 수행
직접 만들 수도 있음
1
2
3
4
5
public static < T , A , R > Collector < T , A , R > of ( Supplier < A > supplier ,
BiConsumer < A , T > accumulator ,
BinaryOperator < A > combiner ,
Function < A , R > finisher ,
Characteristics ... characteristics )
short-circuiting terminal operation
1
2
3
4
5
6
7
8
9
boolean anyMatch ( Predicate <? super T > predicate );
boolean allMatch ( Predicate <? super T > predicate );
boolean noneMatch ( Predicate <? super T > predicate );
Optional < T > findFirst ();
Optional < T > findAny ();
boolean anyMatch(Predicate<? super T> predicate) : 조건을 충족하는 원소가 하나 이상일 경우 true
noneMatch() : 조건을 충족하는 원소가 0개일 경우 true
allMatch() : 모든 원소가 조건을 충족하는 경우 true
parallelStream 관련
parallelStream에서는 원소가 등록된 순서대로 intermediate operation이 진행된다는 보장이 없음(대부분 랜덤하게 진행됨)
sorted(), limit(), reduce() 등의 다른 원소들의 영향을 받는 연산을 parallelStream에서 실행하면 parallelism의 이점이 줄어듬
parallelStream에서 reduce()를 실행하면 쓰레드의 개수만큼 reduce()가 실행되기 때문에 identity 파라미터를 사용할 때 주의해야 함(누적 합일 경우 identity가 쓰레드의 개수만큼 더해짐)
terminal operation으로 forEach()를 사용하면 병렬 처리되는 순서대로 실행되기 때문에, encounter order로 실행하기 위해선 forEachOrdered()를 사용해야 함
collector() 또한 reduce()와 같이 combiner가 있는 오버로딩이 존재함
1
2
3
< R > R collect ( Supplier < R > supplier ,
BiConsumer < R , ? super T > accumulator ,
BiConsumer < R , R > combiner );
collect(Collectors.toList())는 encounter order로 실행됨
Tags:
Accumulator ,
allMatch ,
anyMatch ,
average ,
collect ,
Collectors ,
Comparator ,
Consumer ,
count ,
filter ,
findAny ,
findFirst ,
flatMap ,
flattening ,
forEach ,
forEachOrdered ,
Function ,
Functional interface ,
ifPresent ,
Intermediate operation ,
Java ,
map ,
mapping ,
max ,
min ,
noneMatch ,
Optional ,
parallelStream ,
peek ,
Predicate ,
reduce ,
sorted ,
Stream ,
sum ,
Supplier ,
Terminal operation
Categories:
Dev
Updated: June 14, 2023
Leave a comment