람다식을 공부하면 람다식과 자주 같이 쓰는 것이 stream입니다. 코테에서도 stream을 잘 사용만 하면 가독성 면이나 편하게 data를 세팅할 수 있습니다. 그래서 이번에는 stream을 공부해 보려고 합니다.
아래의 내용은 이것이 자바다라는 책을 보면서 공부한 내용을 정리 했습니다.
https://product.kyobobook.co.kr/detail/S000061695652
이것이 자바다 | 신용권 - 교보문고
이것이 자바다 | JAVA 17 버전으로 업그레이드해서 돌아왔다! 7년 동안 꾸준히 사랑받은 자바 베스트셀러 1위, 『이것이 자바다』 개정판!『이것이 자바다』는 기본 개념에 충실한 설명으로 2015년
product.kyobobook.co.kr
스트림이란?
- List를 탐색할 때는 for문으로 하나씩 처리합니다.
- Set에서는 요소를 하나씩 처리하기 위해 Iterator를 사용합니다.
Set<String> set = ..;
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
String item = iterator.next();
}
하지만 Java 8부터는 또 다른 방법으로 컬랙션 및 배열의 요소를 반복 처리하기 위해 stream을 사용합니다.
Stream<String> stream = list.stream();
stream.forEach( item -> //item 처리 );
Stream과 Iterator의 차이
stream과 iterator는 비슷한 반복자이지만, 차이점이 존재합니다.
- 내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적이다.
- 람다식으로 다양한 요소 처리를 정의할 수 있다.
- 중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성할 수 있다.
내부 반복자
for문과 Iterator는 컬렉션의 요소를 컬렉션 바깥쪽으로 반복해서 가져와 처리합니다. 이것을 외부 반복자라고 합니다.
스트림은 요소 처리 방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리하는데, 이것을 내부 반복자라고 합니다.
- 외부 반복자일 경우는 컬랙션의 요소를 외부로 가져오는 코드와 처리하는 코드를 모두 개발자 코드가 가지고 있어야 합니다.
- 내부 반복자일 경우는 개발자 코드에서 제공한 데이터 처리 코드(람다식)를 가지고 컬랙션 내부에서 요소를 반복 처리합니다. 또 내부 반복자는 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업도 가능합니다.
Stream<String> parallelStream = list.parallelStream(); // 병렬 스트림 얻기
중간 처리와 최종 처리
스트림은 하나 이상 연결될 수 있습니다. 컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림, 매핑 중간 스트림 이렇게 여러 가지를 연결할 수 있습니다. 그 후 최종 스트림인 집계 처리하는 스트림이 붙고 결과를 처리합니다. 이렇게 스트림이 연결되어 있는 것을 스트림 파이프라인이라고 합니다.
- 예를 들어 Student 스트림을 얻고, 중간 처리를 통해 score 스트림으로 변환한 후 최종 집계 처리로 score 평균을 구하는 과정을 구현해 보겠습니다.
double avg = list.stream()
.mapToInt(student -> student.getScore())
.average()
.getAsDouble();
리소스로부터 스트림 얻기
java.uti.stream 패키지에는 스트림 인터페이스들이 있다. BaseStream 인터페이스를 부모로 한 자식 인터페이스들은 상속 관계를 가집니다. BaseStream 안에는 Stream, IntStream, LongStream, DoubleStream이 존재합니다.
- Stream : 객체 요소를 처리하는 스트림
- IntStream, LongStream, DoubleStream : 각각 기본 타입인 int, long, double 요소를 처리하는 스트림입니다.
리턴 타입 | 메소드(매개변수) | 리소스 |
Stream<T> | java.util.Collection.steam() java.util.Collection.parallelStream() |
List 컬렉션 Set 컬렉션 |
Stream<T> IntStream LongStream DoubleStream |
Arrays.stream(T[ ]) Stream.of(T[ ]) Arrays.stream(int[ ]) IntSream.of(int[ ]) Arrays.stream(long[ ]) LongStream.of(long [ ]) Arrays.stream(double[ ]) DoubleStream.of(double[ ]) |
배열 |
IntStream | IntStream.range(int, int) IntStream.rangeClosed(int, int) |
int 범위 |
LongStream | LongStream.range(long, long) LongStream.rangeClosed(long, long) |
long 범위 |
컬랙션으로부터 스트림 얻기
List<Integer> list = new ArrayList<>();
Stream<Integer> stream = list.stream();
배열로부터 스트림 얻기
String[] strArray = { "홍길동", ... }
Stream<String> strStream = Arrays.stream(strArray);
숫자 범위로부터 스트림 얻기
IntStream stream = IntStream.rangeClosed(1, 100);
요소 걸러내기(필터링)
필터링은 요소를 걸러내는 중간 처리 기능이다. 필터링 메소드에는 다음과 같이 distinct()와 filter()가 있다.
인터페이스 | 메소드(매개변수) | 설명 |
Stream IntStream LongStream DoubleStream |
distinct() | - 중복제거 |
filter(Predicate<T>) filter(IntPredicate) filter(LongPredicate) filter(DoublePredicate) |
- 조건 필터링 - 매개 타입은 요소 타입에 따른 함수형 인터페이스이므로 람다식으로 작성 가능 |
- distinct() 메소드는 요소의 중복을 제거한다. (객체 스트림일 경우 equals () 메소드의 리턴값이 true 이면 동일 요소로 판단)
- filter() 메소드는 매개값으로 주어진 Predicate가 true를 리턴하는 요소를 필터링
요소 변환(매핑)
매핑은 스트림의 요소를 다른 요소로 변환하는 중간 처리 기능이다.
요소르 다른 요소로 변환
mapXxx() 메소드는 요소를 다른 요소로 변환한 새로운 스트림을 리턴한다.
- 원래 스트림 A, B를 새로운 요소 C. D로 변경 후 C, D 스트림을 생성한다.
리턴 타입 | 메소드(매개변수) | 요소 -> 변환 요 |
Stream<R> | map(Function<T, R>) | T -> R |
IntStream LongStream DoubleStream |
mapToInt(ToIntFunction<T>) | T -> int |
mapToLong(ToLongFunction<T>) | T -> long | |
mapToDouble(ToDoubleFunction<T>) | T -> double | |
Stream<U> | mapToObj(IntFunction<U>) | int -> U |
mapToObj(LongFunction<U>) | long -> U | |
mapToObj(DoubleFunction<U>) | double -> U | |
DoubleStream DoubleStream IntStream LongStream |
mapToDouble(IntToDoubleFunction) | int -> double |
mapToDouble(LongToDoubleFunction) | long -> double | |
mapToInt(DoubleToIntFunction) | double -> int | |
mapToLong(DoubleToLongFunction) | double -> long |
List<Student> studentList = new ArrayList<>();
sudentList.stream()
.mapToInt(s -> s.getScore())
.forEach(score -> System.out.println(score));
기본 타입 간의 변환이거나 기본 타입 요소를 래퍼 객체 요소로 변환하려면 다음과 같은 간편화 메소드를 사용할 수 있다.
리턴 타입 | 메소드(매개변수) | 설명 |
LongStream | asLongStream() | int -> long |
DoubleStream | asDoubleStream() | int -> double long -> double |
Stream<Integer> Stream<Long> Stream<Double> |
boxed() | int -> Integer long -> Long double -> Double |
요소를 복수 개의 요소로 변환
flatMapxx() 메소드는 하나의 요소를 복수 개의 요소들로 변환한 새로운 스트림을 리턴한다.
리턴 타입 | 메소드(매개 변수) | 요소 -> 변환 요소 |
Stream<R> | flatMap(Function<T, Stream<R>>) | T -> Stream<R> |
DoubleStream | flatMap(DoubleFunction<DoubleStream>) | double -> DoubleStream |
IntStream | flatMap(IntFunction<IntStream>) | int -> IntStream |
LongStream | flatMap(LongFunction<LongStream>) | long -> LongStream |
DoubleStream | flatMapToDouble(Function<T, DoubleStream>) | T -> DoubleStream |
IntStream | flatMapToInt(Function<T, IntStream>) | T -> IntStream |
LongStream | flatMapToLong(Function<T, LongStream>) | T -> LongStream |
//문장 스트림을 단어 스트림으로 변환
List<String> list1 = new ArrayList<>();
list1.add("this is java");
list1.add("i am a best developer");
list1.stream().
flatMap(data -> Arrays.stream(data.split(" ")))
//문자열 숫자 목록 스트림을 숫자 스트림으로 변환
List<String> list2 = Arrays.asList("10, 20, 30", "40, 50");
list2.stream()
.flatMapToInt(data -> {
String[] strArr = data.split(",");
int[] intArr = new int[strArr.length];
for(int i = 0; i< strArr.length; i++) {
intArr[i] = Integer.parseInt(strArr[i].trim());
}
return Arrays.stream(intArr);
})
'BackEnd > JAVA 공부' 카테고리의 다른 글
Java 8, 11, 17 (0) | 2023.08.03 |
---|---|
JVM, JRE, JDK에 대해서 (0) | 2023.08.03 |
람다식이란? (0) | 2023.07.09 |
자바의 컬렉션 프레임워크(Map) (1) | 2023.01.10 |
자바의 컬렉션 프레임워크(Set) (0) | 2023.01.10 |