위로
아래
스트림
스트림 (Stream) 특징
- 스트림 = 데이터의 흐름
- 배열 또는 콜렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 얻을 수 있다
- 스트림을 이용하면 콜렉션(Collection) 데이터를 선언형으로 처리할 수 있다. (간결하고 가독성이 좋아진다)
- 병렬처리 (멀티 스레딩) 가능
- 자바 8 API에서 추가된 기능
- 스트림 연산 = 매개변수 값이 람다식인 함수형 연산
- 스트림 연산은 스트림 데이터를 필터링, 정렬, 매핑, 집계한다
- 스트림 연산은 어떻게 구현할지가 아니라, 어떤 동작을 수행시킬지에 집중되어 있어, 반복문을 어떻게 사용할지 고민하지 않아도 된다
- 스트림은 원소에 직접 접근하거나 조작할 수 없다.
콜렉션과 스트림 비교
구분 | 콜렉션 | 스트림 |
처리 방식 | 다운로드 | 스트리밍 |
저장 공간 | 필요 | 불필요 |
반복 방식 | 외부 반복(반복문, 반복자 iterator 등) | 내부 반복(next() 메소드나 인덱스 필요 X) |
코드 구현 | 명령형 | 선언형 (람다식) |
원본 데이터 | 변경 | 변경하지 않고 소비 |
연산 병렬화 | 어려움 | 쉬움 |
예시
더보기
import java.util.*;
public class StreamDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random r = new Random();
for(int i=0; i<10; i++) {
list.add(r.nextInt(30));
}
//콜렉션으로 처리
List<Integer> gt10 = new ArrayList<>();
for(int i : list) {
if(i>10) {
gt10.add(i);
}
}
Collections.sort(gt10);
System.out.println(gt10);
//스트림으로 처리
list.stream()
.filter(i -> i >10)
.sorted()
.forEach(x -> System.out.print(x + " "));
}
}
스트림의 종류
- BaseStream
- 인터페이스와 4개의 자식 인터페이스로 이루어져 있다.
- java.base 모듈에 포함된 java.util.stream 패키지에 포함되어 있다.
- 객체 스트림
- 객체 원소 처리
- Stream 인터페이스
- 숫자 스트림
- 숫자 타입 원소 처리
- IntStream, LongStream, DoubleStream 인터페이스
- average() 평균 반환, sum() 합계 반환, summaryStatistics() 데이터 스트림의 기본 통계 요약 내용
예시
더보기
import java.util.Arrays;
import java.util.stream.*;
public class Array2StreamDemo {
public static void main(String[] args) {
//정수 배열 이용, IntStream 생성
int[] ia = {2,3,5,7,11,13};
IntStream is = Arrays.stream(ia);
// 문자열 배열 이용, Stream<String> 생성
String[] strings = {"The","pen","is","mighter","than","the","sword"};
Stream<String> ss = Stream.of(strings);
// 실수 배열 이용, DoubleStream 생성
double[] da = {1.2, 3.14, 5.8, 0.2};
DoubleStream ds = DoubleStream.of(da);
}
}
스트림 생성
배열로부터 스트림 생성
// Arrays.stream() 이용 스트림 생성
int[] ia = {1, 2, 3};
IntStream s = Arrays.stream(ia);
// Stream.of() 이용 스트림 생성
String[] sa = {"abc", "def", "ghi");
Stream<String> s = Stream.of(sa);
// DoubleStream.of() 이용 스트림 생성
Double[] da = {1.1, 2.2, 3.3};
DoubleStream s = DoubleStream.of(ds);
// .stream() 이용
Stream<String> s = list1.stream();
기타 데이터로부터 스트림 생성
- Random 클래스 : 숫자 무한 스트림 생성
- iterate() : 연속된 일련의 값으로 이루어진 무한 스트림 생성
- generate() : 비연속적인 값으로 이루어진 무한 스트림 생성
- empty() : 빈 스트림 생성
- range(), rangeClosed() : 정수 스트림에서, 숫자 범위로부터 스트림 생성
스트림 파이프라인
list.stream()
.filter(i -> i >10)
.sorted()
.forEach(x -> System.out.print(x + " "));
- 스트림 파이프라인 : 여러 개의 스트림이 연결된 것
- 스트림 연속 호출 : 스트림 연산의 결과가 Stream 타입이면 연속적으로 호출할 수 있다.
- 중간 연산(intermidate operations)
- 반환 타입은 Stream이다
- filter(), sorted(), skip()
- 느긋한 연산 (lazy operation)
- 최종 연산(terminal operations)
- 반환 타입이 Stream 타입이 아니다
- forEach()
- 조급한 연산 (eager operation)
느긋한 연산 (lazy operation)
- 최종 연산이 실행되지 않으면 연산을 수행하지 않고 기다린다
- 원소를 한꺼번에 처리하지 않고 하나씩 처리해서 바로 보낸다.
- 필터링, 매핑, 정렬 등이 있다.
- 스트림의 중간 연산이 느긋한 연산이기 때문에 스트리밍 방식처럼 저장 공간이 필요 없게 된다 (따라서 빅데이터나 무한 스트림에도 대응 가능)
조급한 연산 (eager operation)
- 스트림 데이터를 마무리하는 역할. (최종 연산)
- 매칭, 카운팅, 최댓값, 최솟값, 평균, 합계, 집계, 리듀싱, 수집 등이 있다.
예시
더보기
import java.util.stream.IntStream;
public class Laziness1Demo {
public static void main(String[] args) {
IntStream s = IntStream.rangeClosed(1, 5);
s.filter(x -> { // 느긋한 연산
System.out.println("filter : " + x);
return x % 2 == 0;
}).map(x -> { // 느긋한 연산
System.out.println("map : " + x);
return x * x;
}).forEach(x -> { // 조급한 연산
System.out.println("forEach : " + x);
});
}
}
parallel stream
병렬 스트림
- 빅데이터를 빠르게 처리하기 위해 데이터 소스를 병렬로 처리할 수 있는 기능을 지원.
- 병렬 스트림이 아닌 스트림을, 순차 스트림이라고 부른다.
- 전체 스트림을 부분 스트림으로 분할한 후, 각 스트림을 개별 스레드가 담당하게 함으로써 병렬로 처리.
- 병렬 스트림을 사용하면 스레드 개수, 동기화 문제를 고민할 필요 없이 데이터를 병렬로 처리 가능
사용 방법
- parallelStream() 메소드
- parallel() 메소드
순차 스트림과 병렬 스트림의 속도차
더보기
import java.util.stream.IntStream;
public class ParallelDemo {
public static void main(String[] args) {
long start, end, total;
IntStream s1 = IntStream.rangeClosed(1, 100000000);
start = System.currentTimeMillis();
total = s1.sum();
end = System.currentTimeMillis();
System.out.println("순차 처리 : " + (end - start));
IntStream s2 = IntStream.rangeClosed(1, 100000000).parallel();
start = System.currentTimeMillis();
total = s2.sum();
end = System.currentTimeMillis();
System.out.println("병렬 처리 : " + (end - start));
}
}