-
[Java] 스트림(Stream)이란? + 특징, 등장배경Java 2022. 1. 29. 00:01728x90
스트림(Stream)이란?
Stream이란 시냇물이라는 뜻으로 물이 흘러가듯이 데이터 소스가 가진 데이터가 흘러감을 의미합니다.
다양한 데이터 소스(컬렉션, 배열)를 표준화(통일)된 방법으로 다루기 위한 것입니다.
공식문서에서는 아래와 같이 소개하고 있습니다.
Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections.
컬렉션의 맵리듀스 변환과 같은 스트림의 요소에 대하여 함수형 스타일의 연산을 지원하는 클래스입니다.맵리듀스(map-reduce)란?
대용량 데이터 처리를 분산 병렬 컴퓨팅에서 처리하기 위한 목적으로 제작된 프레임워크
스트림의 등장배경
다양한 데이터 소스들을 출력하기 위해 방법들이 달랐습니다.
데이터소스들을 표준화된 방법으로 사용하기 위해 등장하였습니다.
또한 멀티코어 CPU 대중화와 같은 하드웨어적인 변화와 빅데이터를 처리하기 위하여 등장하게 되었습니다.
스트림 수행과정
1. 데이터 소스를 스트림으로 만들기
2. 중간 연산 : 연산 결과가 스트림인 연산 (여러 번 할 수 있습니다.)
3. 최종 연산 : 연산 결과가 스트림이 아닌 연산(한 번만 할 수 있습니다.)
stream.distinct().limit(5).sorted().forEach(System.out::println)
위의 코드에서 distinct(), limit(), sorted()는 중간 연산에 해당하며 forEach()는 최종 연산에 해당합니다.
distinct()는 중복제거를 의미하고, limit(5)는 5개 자르기, sorted()는 정렬을 forEach() Stream을 하나씩 꺼내서 출력하는 것을 의미합니다.
스트림의 연산이 두 가지로 나누는 이유?
스트림은 0~n번의 중간 연산과 1번의 최종 연산으로 이루어집니다.
중간 연산의 중요한 특징은 최종 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는 lazy(게으른)한 연산입니다.
중간 연산에 무한한 연산이 들어가는 경우에는 사실 말이 안 되지만 limit(n) 또는 findFirst()과 같은 연산을 사용하면 lazy 한 연산이기 때문에 유한한 시간 내에 연산이 완료될 수 있습니다.
이를 활용하여 연산에 효율성이 상당히 증가하게 됩니다.
데이터 소스를 스트림으로 변환하는 방법 맛보기
List<Integer> list = Arrays.asList(1, 2, 3 ,4 ,5); Stream<Integer> intStream = list.stream(); Stream<String> strStream = Stream.of(new String[] {"a", "b", "c"}); Stream<Integer> evenStream = Stream.iterate(0, n->n+2); Stream<Double> randomStream = Stream.generate(Math::random);
스트림의 특징
1. 스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다. (Read Only)
List<Integer> list = Arrays.asList(3, 1, 5 ,4 ,2); List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList()); //list를 정렬하여 새로운 List에 저장 System.out.println(list); // [3, 1, 5, 4, 2] System.out.println(sortedList); // [1, 2, 3, 4, 5]
2. 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 한다.)
strStream.forEach(System.out::println); //모든 요소를 화면에 출력(최종연산) int numOfStr = strStream.count(); //에러발생 최종연산이 끝나고 스트림이 닫혀버림
3. 최종 연산 전까지 중간 연산이 수행되지 않는다. - 지연된 연산
어떤 연산을 해야 하는지만 체크해두었다가 나중에 연산을 수행합니다.
중간 연산에 무한한 연산이 들어가는 경우에는 사실 말이 안 되지만 limit(n) 또는 findFirst()과 같은 연산을 사용하면 lazy 한 연산이기 때문에 유한한 시간 내에 연산이 완료될 수 있습니다.
4. 스트림은 작업을 내부 반복으로 처리한다
//기존 for문 출력 for(String str : strList){ System.out.println(str); } //스트림 출력 stream.forEach(System.out::println);
forEach() 메서드 안에는 반복문이 선언되어 있습니다.
속도는 느리지만 코드가 간결합니다.
실제로 만 번 반복하여 테스트해보았을 때 3배 정도의 속도 차이가 발생했습니다.
(여기에는 최종 연산 후 스트림이 닫히기 때문에 계속 생성하는 비용도 영향을 많이 미칠 것 같습니다.)
5. 병렬 스트림
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b"); int sum = strStream.parallel().mapToInt(s -> s.length()).sum(); //모든 문자열의 길이의 합
parallel() 메서드를 통하여 간단하게 병렬 스트림으로 전환할 수 있습니다.
이를 통해 멀티스레드 환경에서 빠른 작업이 가능합니다.
출처
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
https://www.youtube.com/watch?v=7Kyf4mMjbTQ
'Java' 카테고리의 다른 글
[Java] Comparator와 Comparable란? (0) 2022.02.03 [Java] 스트림의 생성 (0) 2022.01.31 [Java] 싱글톤(Singleton)이란? (0) 2022.01.27 [Java] 메서드 참조란? (0) 2022.01.25 [Java] Math.random() vs java.util.Random 효율성 차이점 비교 (0) 2022.01.24