[JAVA] 람다와 스트림 ( Lambda & Stream ) ⑥

DongGyu Jung·2022년 5월 1일
0

자바(JAVA)

목록 보기
54/60
post-thumbnail

🏃‍♂️ 들어가기 앞서..

본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다.
※ 스터디 Page : 〔투 비 마스터 : 자바〕

*해당 교재의 목차 순서와 구성을 참고하여 작성하며
각 내용마다 부족할 수 있는 내용이나 개인적으로 궁금한 점은
추가적인 검색을 통해 채워나갈 예정입니다.



스트림(Stream)

: 다양한 데이터 소스( Collections, Arrays )를 표준화된 방법으로 다루기 위한 것

물론 Collection 이나 Iterator와 같은 인터페이스를 통해 컬렉션을 다루는 방식이 표준화되어 있긴 하지만 "같은 기능의 메서드들 중복 정의 " 등과 같이 많이 부족한 면이 있다.

스트림(Stream)은
데이터 소스를 추상화하고

다루는데 자주 사용되는 메서드들을 정의해 놓았다.

추상화를 통해
데이터 소스가 무엇이던 간에 같은 방식으로 다룰 수 있기 때문에
완전한 통일, 완전한 표준화를 이뤄낼 수 있었다.

자연스레 표준화가 되었기 때문에
코드의 재사용성이 높아지는 효과가 있다.

스트림을 이용하는 방법은
다음과 같다.

List<Integer> list = Arrays.asList(1,2,3,4,5) ; // 컬렉션
Stream<Integer> intStream = list.stream() ; // 컬렉션 -> Stream
Stream<String> strStream = Stream.of( new String[]{"a", "b", "c"} ) ; // 배열 -> Stream

Stream<Integer> evenStream = Stream.iterate(0, n->n+2) ; // 람다식 -> Stream 
Stream<Double> randomStream = Stream.generate(Math::random) ; // 메서드 참조 : 람다식 -> Stream

// Int형 최적화 Stream : IntStream
IntStream intStream = new Random().ints(5) ; // 난수 스트림

※ Stream 작업 처리 3단계
: Stream 만들기 중간 연산 (내부 반복 작업) : 0~n번 최종 연산 (결과 출력) : 0~1번

  • 중간 연산 : [연산 결과] 스트림 _ 반복적으로 적용 가능
  • 최종 연산 : [연산 결과] 스트림 X _ 단 한 번만 적용가능 (스트림 : 일회성 → 소모됨)
/*
- distinct = 중복제거
- limit = ~개 까지만 
- sorted = 정렬

- forEach = 각 요소마다 
*/
stream.distinct().limit(5).sorted().forEach(System.out::println) ;
//    |----------중간연산----------| |---------최종연산----------|


🎡 스트림 특징

1. 데이터 소스를 변경하지 않는다.

Read Only (Like SQL SELECT)

: DB에서 직접 데이터를 변경하지 안혹 조회만 하는 SELECT의 경우와 비슷하고
적용되는 메서드는 변경 메서드가 아닌 주로 조회/취급 조건을 거는데에 사용되는 메서드이다.

List<Integer> list = Arrays.asList(3,1,5,4,2) ;
List<Integer> sortedList = list.stream().sorted().collect(Collections.toList()); // 정렬해서 새로운 List에 저장

System.out.println(list); // [3,1,5,4,2] 
// list에 .을 통해 여러 함수를 적용했음에도 실제로 변경되지 않는다.
System.out.println(sortedList) ; // [1,2,3,4,5]

2. 일회용

Iterator 처럼 일회용으로
필요하면 다시 Stream을 생성해야 한다.


3. " 내부 반복 "으로 작업 처리

" 내부 반복 " : 단순하게 for문이나 while문 처럼 블록을 활용해서 반복 시키는 것이 아닌
forEach와 같이 반복문을 내부에 숨긴 메서드들을 통해 간략하게 반복문을 처리하는 것

성능은 비효율적이지만
반복문을 메서드 안에 넣어버림으로서

스트림을 이용한 작업을 작성하는 코드가 간결해진다.


4. 지연된 연산

앞서 스트림은 생성 이후,
중간 연산과 최종 연산 단계를 거친다는 것을 알 수 있었다.

여기에서 지연된 연산이라는 것은
중간 연산 과정에서
각 연산을 하나씩 독립적으로 수행되는 것이 아니라
최종 연산 전까진 수행되지 않다가

최종 연산이 수행되어야
비로소 해당 스트림의 요소들이 중간 연산을 거쳐
최종 연산에서 소모된다.


5. Stream<Integer> & IntStream

기본적으로
" 요소의 타입이 T인 스트림 " Stream<T> 으로 사용되는데
이 T에는 기본형이 아닌 참조형만 가능하다.

그래서 자세히 살펴보면

int[] intArr = { 1, 2, 3 } ;
Stream<Integer> intStream = Arrays.stream(intArr) ;

이렇게 변환할 때,
기본형(int)였던 1, 2, 3 등의 값들이
new Integer(1), new Integer(2), new Integer(3) 으로
즉, 참조형으로 바뀌어서 저장 ( 오토 박싱 )이 된다.

그렇기 때문에
이런 오토박싱 & 언박싱으로 인한 비효율적인 처리를 방지하기 위해

【기본형 스트림】IntStream, LongStream, DoubleStream ...등등
을 제공한다.

"""
데이터 소스가 기본형일 때
"""
이 " 기본형 스트림 "을 사용하면
처리 효율 측면에서도 더욱 효율적이고

타입에 맞춰 적용되어야 하는 Stream<T> 보다
이미 타입을 알고 최적화되어 있는 기본형 스트림에는
해당 기본형 값을 처리하는데 유용한 메서드들이 포함되어 있다. (ex. sum(), average() ..등)


6. 병렬 Stream

: 멀티 쓰레드(Multi-Thread)를 통해 병렬 작업 처리를 수행하기 쉽다.

함수형 프로그래밍이 주목받기 시작한 것이 "빅데이터" 즉, 대량의 데이터에 대한 작업에서 멀티 쓰레드를 통한 빠른 처리 덕분이라 했는데
이러한 기능을 수행하기 위해 사용되는 것이 병렬 스트림이라고 할 수 있다.

Java 자체 내부적으로 fork&join 프레임 웍을 이용해서
자동적으로 연산을 병렬로 수행한다.

사용자는 그저 Stream에
parallel()이라는 메서드를 호출해서 지시하기만 하면 된다.
parallel() 취소 : sequential()

일단 기본적으로 Stream은 병렬 스트림이 아니기 때문에
sequential()parallel()을 사용하고 취소할 때만 사용하면 되고

알고 있어야할 것은
위 메서드 [sequential() / parallel()]는

새로운 스트림을 만드는 것이 아니라
속성을 변경하는 것이다.

0개의 댓글