학습 키워드
- 스트림
- 스트림의 활용
- 스트림의 주요 메서드
스트림, Stream
>의미
: 데이터 소스를 가공하여 결과값을 도출하는 흐름
: 데이터 소스의 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
>특징
- 1. 선언형 프로그래밍을 할 수 있다.
: 프로그램이 '어떤 방법으로 해야 하는지'보다는 '무엇을 수행하는지'를 설명하는 프로그래밍
: 즉, 문제 처리의 과정인 알고리즘을 보여주는 것이 아니라, 프로그램의 목표를 알려주는 프로그래밍
- 2. 요소 처리 코드를 람다식으로 표현할 수 있다.
- 3. 중간 연산과 최종 연산을 수행한다.
: 지연된 연산; 최종 연산이 실행될 때 까지 연산이 지연된다.
: 중간 연산의 반환값은 Stream이고, 최종 연산의 반환값은 Stream이 아닌 자료형의 값이다. (연산이 지연된다는 의미
의 이유인 것 같음)
- 4. 원본을 변경하지 않는다. (READ-ONLY)
- 5. 병렬 처리가 쉽다
: 내부 반복자를 사용할 수 있기 때문에
: 멀티 코어 CPU를 효율적으로 활용할 수 있다. (내부 데이터를 바꾸지 않기 때문에 병렬 처리가 가능한 것 같다)
/*
외부 반복자와 내부 반복자
외부 반복자
: 개발자가 코드로 직접 요소를 반복해서 가져오는 것
: for, while, Iterator 들이 외부 반복자를 이용하는 것
내부 반복자
: 개발자는 요소당 처리할 코드만 제공하고 반복은 컬렉션 내부에서 진행하는 것.
: 아직 확실한 이해는 못했지만, 외부 반복자는 값을 직접 가져와서 사용하는 코드이고, 내부 반복자는 사람의 눈으로는 읽
기만 하는 코드로 볼 수 있다.
: 콜렉션 내부에서 반복을 처리한다는 의미가 곧 콜레션을 변경하지 않고 연산을 수행하는 것을 의미하는 것 같다.
*/
스트림의 활용
>스트림 사용 단계
- 스트림 생성
- 중간 연산
- 최종 연산
//각 단계에 해당하는 메소드들이 많다. 자주 쓰는 것은 외우고, 필요할 때마다 검색하면 될 듯.
//각 단계 메서드 정리
: https://hso8706.tistory.com/62
>스트림 생성
: 파이프로 흘려보낼 데이터를 일렬로 정렬시키는 과정
: 스트림 생성 메서드를 사용하면 다양한 데이터 소스들을 Stream의 객체(인스턴스)로 반환시킨다.
: 데이터 소스들을 인스턴스로 만든 것이기 때문에 실제 값은 변하지 않고 읽기만 하는 것이다.
: IntStream, LongStream, DoubleStream과 같은 기본 자료형을 위한 특수한 종류의 Stream이 존재한다.
: 각각의 반환 타입마다 메서드가 다르기 때문에 적절하게 써주는 것이 좋다.
: 또한, 기본 자료형을 다룰 경우 위의 특수 Stream을 써주면 불필요한 과정(예: 박싱, 언박싱)을 안할 수 있으므로 좀 더 효율적이다.
>스트림 중간 연산
: 생성한 스트림을 필터링하는 과정(가공하는 과정)
: 다양한 메서드를 통해 Stream 인스턴스로 반환
: 최종 연산까지 필요한 만큼 중간 연산을 해도 되고, 중간 연산을 생략해도 된다.
: 중간 연산의 순서는 상관없다. (값은 달라질 수도 있지만)
>스트림 최종 연산
: 필터링된 스트림(가공된 스트림)을 사용할 수 있는 데이터로 만드는 과정.
: Stream이 아닌 자료형의 값으로 반환
: 정확히는 값 또는 현상으로 반환
: 최종 연산은 마지막 단계에서 딱 한 번만 가능함.
: 최종 연산 이후에 스트림은 사라짐, 재사용 불가. (인스턴스)
>스트림 코드 예시
1. 기본
public class Main {
public static void main(String[] args) {
//데이터 소스; List<Stream> 타입;
List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F");
//1. Stream 생성; Collection.stream() 메서드 이용; 요소 타입<String>; 반환 타입 Stream<String>
Stream<String> listStream = list.stream();
//2-1. 중간 연산1; pipe1; "B" 제거; 요소 타입<String>; 반환 타입 Stream<String>
Stream<String> pipe1 =listStream.filter(n -> !n.equals("B"));
//2-2. 중간 연산2; pipe2; "D", "F" 제거; 요소 타입<String>; 반환 타입 Stream<String>
Stream<String> pipe2 =pipe1.filter(n -> !n.equals("D") && !n.equals("F"));
//3. 최종 연산; Collectors.toList()메서드 이용; List<String>으로 반환
List<String> result = pipe2.collect(Collectors.toList());
System.out.println(result);
/*
[A, C, E]
*/
}
}
2. 메서드 체이닝 이용
public class Main {
public static void main(String[] args) {
//데이터 소스; List<Stream> 타입;
List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F");
//메서드 체이닝 이용
//최종 결과의 참조형과 참조변수에 모든 Stream처리 과정을 작성.
//문장이 길 경우, 개행과 들여쓰기로 표현(가독성)
//최종 연산 후에 세미콜론(;) 사용
List<String> result = list.stream()
.filter(n -> !n.equals("B"))
.filter(n -> !n.equals("D") && !n.equals("F"))
.collect(Collectors.toList());
System.out.println(result);
/*
[A, C, E]
*/
}
}
Stream 공식 문서
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Optional<T>
>의미
: Null 값으로 인해 에러가 발생하는 현상을 방지하고, 이를 위해 사용하는 객체
: NullPointerException(NPE)
: 모든 타입의 객체를 담을 수 있는 Wrapper 클래스
: 다양한 메서드가 존재하고 이는 Stream의 메서드와 유사
>사용
: 메서드 체이닝(여러 메서드를 연결함)을 통해 스트림처럼 사용할 수 있음.
: Stream 객체를 생성하는 것 대신, Optional<T>의 객체로 데이터를 리스트화해서 사용. (스트림 생성하듯)
: Stream의 최종 연산 메서드의 반환값으로 사용.
//스트림을 사용할 때도 중간 연산에 Null을 방지하는 조건을 넣어주거나, try-catch문으로 잡거나, 맨처음 If로 엣지 잡아주
면 되긴 할텐데... Optional<T>의 사용은 취향일까? 아님 필요한 곳이 꼭 있을까? 편한지는 좀 써봐야 알 것 같다.
//우선은 있는 것만 알아두고, 필요할 때 깊게 공부하자.
Optional 공식 문서
https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
'코드스테이츠_국비교육 > [Section1]' 카테고리의 다른 글
18.04_메서드 레퍼런스_22.09.15 (0) | 2022.09.17 |
---|---|
18.03_스트림 메서드_22.09.15 (0) | 2022.09.17 |
18.01_람다_22.09.15 (0) | 2022.09.15 |
16.10_컬렉션 프레임워크[Map]_22.09.13 (0) | 2022.09.15 |
16.09_컬렉션 프레임워크[Set<E>]_22.09.13 (0) | 2022.09.15 |