스트림(Stream)
컬렉션의 데이터를 표준화 하여 쉽게 다루기 위한 도구
컬렉션,배열 --> 1. 스트림 오픈 --> 2. 중간 연산 --> 3. 최종 연산
- 스트림은 데이터를 읽기만 할 뿐 변경시키지 않음(Read Only)
- 일회용 such as Iterator
- 내부 반복자를 통해 처리 cf) 외부 반복자 : Iterator
- 최종 연산 전까지 중간 연산이 실행되지 않는다(lazy)
1. 스트림 오픈
- ex) student.stream();
2. 중간 연산
- 필터링, 매핑(변환), 정렬, 그룹핑, 루핑(순환)
- 리턴타입 : 스트림
(1) 필터링
-
지네릭이 바뀌지 않음
-
distinct()
: 중복 제거 -
filter(Predicate predicate)
: 필터링
(2) 변환
-
지네릭이 바뀜
-
map(Function function)
: 컬렉션의 element 맵핑- ex)
map(Student::getName)
: Student -> String
- ex)
-
flatMap(Consumer consumer)
: 컬렉션 내 컬렉션의 element 맵핑- ex)
flatMap(Arrays::stream)
: Array<String> -> String
- ex)
String[][] arr = {
{"a", "b", "c"},
{"d", "e", "f"}
};
Arrays.stream(arr)
.flatMap(Arrays::stream)
.collect(Collectors.toList());
(3) 정렬
-
Comparable을 구현 한 클래스에서만 사용 가능, Comparable이 구현되지 않은 클래스는 예외 발생
-
sorted()
: 기본 조건 정렬 -
sorted(Comparator comparator)
: Comparator 조건 정렬
(4) 조회
-
peek()
: 중간 처리 결과를 출력, 중간 작업결과 확인용으로 사용
3. 최종 연산
- 스트림의 element를 소모해 결과 생성
- 매칭, 집계(합계, 평균, 카운팅, 최대값, 최소값), 수집, 루핑
- 리덕션 : 대량의 데이터를 가공해서 축소하는 것 ex) 합계, 평균, 카운팅, 최대값, 최소값
- 메소드 : 리턴타입이 기본 타입 or Optional__
→ Optional__ : 타입이 래퍼 클래스임(언박싱이 필요함)
(1) 매칭
-
요소들이 조건에 만족하는지 확인
-
__Match()
: 조건이 만족하는지 확인-
allMatch()
,anyMatch()
,noneMatch()
-
-
find__()
: 조건일치 요소 반환findFirst()
-
findAny()
: 병렬 스트림에서 사용
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); // true
result = Arrays.stream(intArr).anyMatch(a -> a%3 == 0);
System.out.println("하나라도 3의 배수인가 ? " + result); // true
result = Arrays.stream(intArr).noneMatch(a -> a%3 == 0);
System.out.println("3의 배수는 없는가 ? " + result); // false
}
(2) 집계
-
sum()
,average()
,count()
,max()
,min()
-
reduce()
: 커스텀 합계- 요소를 하나씩 줄여나가며 계산
(3) 수집
-
주로 요소를 그룹화하거나 분활한 결과를 컬렉션에 담아 반환할 때 사용
collect(Collector collector)
-
Collectors
: 이미 구현되어 있는 Collector 인터페이스 구현체-
toList()
,toSet()
,toMap(key, value)
,toCollection()
,toArray()
등
-
- groupingBy(Function) / partitioningBy(Predicate) : 스트림 ->Map
- groupingBy(key, value) & partitioningBy(key, value) 라고 생각
// 1. 단순분할(성별)
Map<Boolean, List<Student2>> stuBySex = Stream.of(stuArr)
.collect(Collectors.partitioningBy(Student2::isMale));
List<Student2> maleStudent = stuBySex.get(true);
List<Student2> femaleStudent = stuBySex.get(false);
// 2. 단순분할 + 통계(성별 학생수)
Map<Boolean, Long> stuNumBySex = Stream.of(stuArr)
.collect(Collectors.partitioningBy(Student2::isMale, Collectors.counting()));
System.out.println("남학생 수 : " + stuNumBySex.get(true));
System.out.println("여학생 수 : " + stuNumBySex.get(false));
// 3. 단순분할 + 통계(성별 1등)
Map<Boolean, Optional<Student2>> topScoreBySex = Stream.of(stuArr)
.collect(Collectors.partitioningBy(Student2::isMale, Collectors.maxBy(Comparator.comparingInt(Student2::getScore))));
System.out.println("남학생 수 : " + topScoreBySex.get(true));
System.out.println("여학생 수 : " + topScoreBySex.get(false));
// 4. 다중분할(성별, 불합격자)
Map<Boolean, Map<Boolean, List<Student2>>> failedStuBySex = Stream.of(stuArr)
.collect(Collectors.partitioningBy(Student2::isMale, Collectors.partitioningBy(s -> s.getScore() <= 100)));
List<Student2> failedMaleStu = failedStuBySex.get(true).get(true); // 1번get: 성별, 2번get: score<=100
List<Student2> failedFemaleStu = failedStuBySex.get(false).get(true);
○ collect(supplier, accumulator, combiner) 구현
- supplier - Supplier
: 사용자정의 컨테이너 생성 - accumulator - BiConsumer<R, ? super T> : 스트림의 요소를 수집
- combiner - BiConsumer<R, R> : 병렬 스트림의 경우 두 컨테이너를 병합
(4) 루핑
forEach(Consumer consumer)
double avg = list.stream() // 오리지널 스트림 (Collections -> Stream)
.mapToInt(s -> s.getScore()) // 중간 처리 (Stream -> IntStream)
.average() // 최종 처리 (IntStream -> OptionalDouble)
.getAsDouble(); // 언박싱(Wrapper -> 기본(OptionalDouble -> double))
System.out.println("평균 점수 : " + avg);
○ 병렬 처리 : parallelStream()
- 내부적으로 쓰레드를 사용해서 병렬로 처리함
● 스트림의 변환
스트림→기본형 스트림 | Stream→IntStream | mapToInt(Function) | |
기본형 스트림→스트림 |
IntStream→Stream | boxed() | |
기본형 스트림→기본형 스트림 | IntStream→DoubleStream | asDoubleStream() | |
2개 스트림→스트림 | Stream, Stream → Stream | concat(Stream a, Stream b) | |
스트림의 스트림→스트림 | Stream |
flatMapTo__(Function) | |
스트림↔병렬스트림 | parallel() : 스트림→병렬스트림 sequential() : 스트림←병렬스트림 |
||
스트림→컬렉션(List,Set) | Stream→Collection,List,Set | collect(Collectors.to___()) | |
컬렉션→스트림 | Collection,List,Set→Stream | stream() | |
스트림→컬렉션(Map) | Stream→Map | collect(Collectors.toMap(function)) | |
스트림→배열 | Stream->Object[] | toArray() |