스트림이란
데이터의 연속적인 흐름으로, 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
•
다양한 데이터 소스: 컬렉션, 배열
기존에 다양한 데이터 소스를 표준화하는 방법으로 컬렉션 프레임워크가 있었음
하지만 List, Set과 Map의 특성과 사용 방법이 달라 완벽한 표준화라고는 할 수 없음
JDK1.8에서는 다양한 컬렉션과 배열을 같은 연산을 사용하여 결과를 낼 수 가 있게 됨
스트림에 대한 과정은 아래와 같음
컬렉션, 배열 -> 1. stream 만들기 -> 1. 중간 연산 -> ... -> 중간 연산 -> 3. 최종 연산 -> 결과 도출
Java
복사
1. 스트림 만들기
1.
list.stream(); // 컬렉션
2.
Stream.of(new String[]{”a”,”b”}); // 배열
3.
Stream.iterate(0, n→n+2)
4.
Stream.generate(Math::random);
5.
new Random().inits(5)
2. 중간 연산
연산 결과가 스트림인 연산. 반복적으로 적용 가능
stream.distinct().limit(5).sorted().forEach(System.out::println);
Java
복사
•
distinct(): 중복 제거
•
limit(n): n개 자르기
•
skip(n): 앞에서부터 n개 건너띄기
•
sorted(), sorted(Comparator comparator (정렬 기준)): 정렬
◦
Comparator의 comparing()으로 정룔 기준을 제공
.sorted(Comparator.comparing(Student::getBan)) //반벌로 정렬
.forEach(System.out::println);
(s)->s.getBan() 을 Student::getBan으로 표현
Java
복사
•
filter(Predicate predicate(조건식)): 조건에 맞지 않는 요소 걸러내기
•
map(Function mapper): 변환 (Stream<T> → Stream<R>)
•
peek(): forEach()와 같지만 peek()은 중간 연산(스트림 요소를 소비 x), 중간 연산 중에 확인할 때 사용(디버깅 용도)
•
flatmap(): 스트림의 스트림을 스트림으로 변환(합침) ( Stream<String[]> → Stream<String> )
strArrStrm.flatMap(Arrays::stream);
Java
복사
→ string의 단어들을 stream으로 변환하고 싶을 때도 사용
String[] lineArr = {
"Believe or not It is true",
"Do or do not There is no try"
};
String<String> lineStream = Arrays.stream(lineArr);
lineStream.flatMap(line -> Stream.of(line.split(" +"))) //하나이상의 공백
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
/*
believe
do
is
it
no ...
*/
Java
복사
3. 최종 연산
연산결과가 스트림이 아닌 연산. 단 한번만 적용 가능(스트림의 요소를 소모)
스트림이 닫힘
•
forEach(Consumer action): 스트림의 모든 요소에 지정된 작업을 수행, 병렬스트림인 경우 순서가 보장되지 않음
•
forEachOrdered(Consumer action): 스트림의 모든 요소에 지정된 작업을 수행, 병렬스트림인 경우에도 순서가 보장됨
•
allMatch(Predicate predicate): 모든 요소가 조건을 만족시키면 true
•
anyMatch(Predicate predicate): 한 요소가 조건을 만족시키면 true
•
noneMatch(Predicate predicate): 모둔 요소가 조건을 만족시키지 않으면 false
•
findFirst(): 조건에 일치하는 첫번째 요소를 반환, 순차 스트림에 사용
•
findAny(): 조건에 일치하는 아무거나 하나를 반환, 병렬 스트림에 사용
•
reduce(T identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner): 스트림의 요소를 하나씩 줄여가며 누적 연산(accumulator) 수행
◦
identity: 초기값(생략 가능)
◦
accumulator: 이전 연산결과와 스트립의 요소에 수행할 연산
◦
combiner: 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림)(생략 가능)
int a = 0 // identity
for(int b : stream) a = a+b // sum()
-> 이 식을 아래와 같이 표현 가능
intSream.reduce(0, (a,b)-> a+b )
Java
복사
스트림의 특징
•
스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않음
•
스트림은 Iterator처럼 일회용(필요하면 다시 스트림을 생성해야함)
•
최종 연산 전까지 중간 연산이 수행되지 않음 - 지연된 연산
◦
지연된 연산으로 무한 스트림도 연산이 가능함
•
스트림은 작업을 내부 반복으로 처리함
stream.forEach(System.out::println);
Java
복사
→ 코드는 간결해지지만 성능 효율성은 떨어짐
•
스트림의 작업을 병렬로 처리 - 병렬스트림
◦
FP는 빅데이터같은 대작업을 위해 필요
◦
병렬 스트림이란 멀티스레드로 스트림 처리
strStream.parallel() // 병렬 스트림으로 전환(속성만 변경)
.mapToInt(s -> s.length()).sum(); // 모든 문자열 길이의 합 (병렬 스트림으로 하여야 빨리 처리)
Java
복사
•
기본형 스트림 - IntStream, LongStream, DoubleStream
◦
오토박싱 & 언박싱의 비효율이 제거됨(Stream<Integer> 대신 IntStream 사용)
▪
Stream<Integer> 을 사용하면 1 → new Integer(1) 오토박싱 과정을 거침
▪
마찬가지로 Integer → 1로 언박싱하는 과정을 거침
▪
FP는 빅데이터 처리에 효율적이여야 함
▪
그렇기 때문에 오토박싱, 언박싱 하는데 걸리는 시간을 없애야 함
▪
이를 위해 기본형 스트림을 사용
◦
IntStream 같은 경우 숫자와 관련된 유용한 메서드를 Strem<T>보다 더 많이 제공