위로 아래

스트림

스트림 (Stream) 특징

  1. 스트림 = 데이터의 흐름
  2. 배열 또는 콜렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 얻을 수 있다
  3. 스트림을 이용하면 콜렉션(Collection) 데이터를 선언형으로 처리할 수 있다. (간결하고 가독성이 좋아진다)
  4. 병렬처리 (멀티 스레딩) 가능
  5. 자바 8 API에서 추가된 기능
  6. 스트림 연산 = 매개변수 값이 람다식인 함수형 연산
  7. 스트림 연산은 스트림 데이터를 필터링, 정렬, 매핑, 집계한다
  8. 스트림 연산은 어떻게 구현할지가 아니라, 어떤 동작을 수행시킬지에 집중되어 있어, 반복문을 어떻게 사용할지 고민하지 않아도 된다
  9. 스트림은 원소에 직접 접근하거나 조작할 수 없다. 

 

콜렉션과 스트림 비교

구분 콜렉션 스트림
처리 방식 다운로드 스트리밍
저장 공간 필요 불필요
반복 방식 외부 반복(반복문, 반복자 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 + " "));
	}
}

 

 

스트림의 종류

  1. BaseStream
    1. 인터페이스와 4개의 자식 인터페이스로 이루어져 있다.
    2. java.base 모듈에 포함된 java.util.stream 패키지에 포함되어 있다.
  2. 객체 스트림
    1. 객체 원소 처리
    2. Stream 인터페이스
  3. 숫자 스트림
    1. 숫자 타입 원소 처리
    2. IntStream, LongStream, DoubleStream 인터페이스
    3. 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();

 

기타 데이터로부터 스트림 생성

  1. Random 클래스 : 숫자 무한 스트림 생성
  2. iterate() : 연속된 일련의 값으로 이루어진 무한 스트림 생성
  3. generate() : 비연속적인 값으로 이루어진 무한 스트림 생성
  4. empty() : 빈 스트림 생성
  5. range(), rangeClosed() : 정수 스트림에서, 숫자 범위로부터 스트림 생성

 

 

 


스트림 파이프라인

list.stream()
	.filter(i -> i >10)
	.sorted()
	.forEach(x -> System.out.print(x + " "));
  1. 스트림 파이프라인 : 여러 개의 스트림이 연결된 것
  2. 스트림 연속 호출 : 스트림 연산의 결과가 Stream 타입이면 연속적으로 호출할 수 있다.
  3. 중간 연산(intermidate operations)
    1. 반환 타입은 Stream이다
    2. filter(), sorted(), skip()
    3. 느긋한 연산 (lazy operation)
  4. 최종 연산(terminal operations)
    1. 반환 타입이 Stream 타입이 아니다
    2. forEach()
    3. 조급한 연산 (eager operation)

 

느긋한 연산 (lazy operation)

  1. 최종 연산이 실행되지 않으면 연산을 수행하지 않고 기다린다
  2. 원소를 한꺼번에 처리하지 않고 하나씩 처리해서 바로 보낸다.
  3. 필터링, 매핑, 정렬 등이 있다.
  4. 스트림의 중간 연산이 느긋한 연산이기 때문에 스트리밍 방식처럼 저장 공간이 필요 없게 된다 (따라서 빅데이터나 무한 스트림에도 대응 가능)

 

조급한 연산 (eager operation)

  1. 스트림 데이터를 마무리하는 역할. (최종 연산)
  2. 매칭, 카운팅, 최댓값, 최솟값, 평균, 합계, 집계, 리듀싱, 수집 등이 있다. 

 

예시

더보기
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

병렬 스트림

  1. 빅데이터를 빠르게 처리하기 위해 데이터 소스를 병렬로 처리할 수 있는 기능을 지원.
  2. 병렬 스트림이 아닌 스트림을, 순차 스트림이라고 부른다.
  3. 전체 스트림을 부분 스트림으로 분할한 후, 각 스트림을 개별 스레드가 담당하게 함으로써 병렬로 처리.
  4. 병렬 스트림을 사용하면 스레드 개수, 동기화 문제를 고민할 필요 없이 데이터를 병렬로 처리 가능

 

사용 방법

  1. parallelStream() 메소드
  2. 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));
	}
}