-
[JAVA] 스트림(Stream) 기본 / 특징 / 연산 (1)DEV/JAVA 2024. 3. 20. 17:29
(1) 등장배경
컬렉션이나 배열에 데이터를 담고 원하는 결과를 얻기 위해 for문과 Iterator를 이용해 코드를 작성해왔으나,
이러한 코드는 가독성이 떨어지고 재사용성도 떨어진다. 또한 데이터 소스마다 다른 방식으로 다뤄야한다는 문제점과 컬렉션 클래스에 같은 기능의 메서드들이 중복해서 정의되어있다.
이러한 문제점들을 해결하기 위해 만들어진 것이 ‘스트림’ 이다.스트림은 데이터소스를 추상화하고, 자주 사용되는 메서드를 정의해 놓았다.
→ 데이터 소스가 무엇이던 간에 같은 방식으로 다룰 수 있게 됨.
→ 코드의 재사용성이 높아짐
ex) 다음과 같은 배열과 리스트가 존재할때,
String[] strArr = { “aaa”, “bbb”, “ccc” }; List<String> strList = Arrays.asList(strArr);
두 데이터 소스를 기반으로 하는 스트림은 다음과 같이 생성한다.
Stream<String> strStream1 = strList.stream(); Stream<String> strStream2 = Arrays.stream(strArr);
이 두 스트림으로 데이터를 읽어서 정렬하고 화면에 출력하는 방법은 다음과 같다.
strStream1.sorted().forEach(System.out::println); strStream2.sorted().forEach(System.out::println);
이처럼 두 스트림의 데이터 소스는 완전히 다르지만, 정렬하고 출력하는 방법은 완전히 동일하다.
기존에는 아래와 같이 코드를 작성해야한다.
Arrays.sort(strArr); Collections.sort(strList); for(String str : strArr) System.out.println(str); for(String str : strList) System.out.println(str);
(2) 스트림의 특징
1. 스트림은 데이터 소스를 변경하지 않는다.
: 데이터소스로부터 데이터를 읽기만 할 뿐, 데이터를 변경하지않는다.
2. 스트림은 일회용이다.
: 한 번 사용하면 닫혀서 다시 사용할수없다. 필요 시 다시 생성해야한다.
3. 스트림은 작업을 내부 반복으로 처리한다.
: 즉, 반복문을 메서드의 내부에 숨길 수 있다. forEach()는 스트림에 정의된 메서드 중 하나로,
매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용한다.
ex)
/* for문 */ for(String str : strList) { System.out.println(str); } /* 스트림의 forEach */ stream.forEach(System.out::println); //람다식을 메서드 참조로 표현함
→ 이와 같이 forEach()는 메서드 안으로 for문을 넣은것이다.
(3) 스트림의 연산
: 스트림이 제공하는 다양한 연산을 이용해서 복잡한 작업들을 간단히 처리할 수 있다.
- 중간연산 : 연산 결과가 스트림. 스트림에 연속해서 중간 연산이 가능하다.
- 최종연산 : 연산 결과가 스트림이 아님. 스트림의 요소를 소모하므로 단 한번만 가능하다.
* 모든 중간 연산의 결과는 스트림이지만, 연산 전의 스트림과 같은 것은 아니다.
* 지연된 연산 : 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다.
* 스트림의 형태는 기본적으로 Stream<T> 지만, 오토박싱 & 언박싱으로 인한 비효율을 줄이기위해 기본형 스트림인 IntStream, LongStream, DoubleStream이 제공된다.
* 병렬 스트림 : 데이터의 병렬 처리가 쉽다. 사용자가 parallel() 메서드를 호출하기만 하면 되며, 병렬 처리가 항상 더 빠른 결과를 얻게 해주는 것은 아니다.- 스트림의 중간연산 목록
중간연산 설명 Stream<T> distinct() 중복 제거 Stream<T> filter (Predicate<T> predicate) 조건에 안 맞는 요소 제외 Stream<T> limit (long maxSize) 스트림의 일부를 잘라냄 Stream<T> skip (long n) 스트림의 일부를 건너뜀 Stream<T> peek (Consumer<T> action) 스트림의 요소에 작업수행 (중간 스트림 조회용) Stream<T> sorted()
Stream<T> sorted(Comparator<T> comparator)스트림의 요소를 정렬 Stream<R> map(Function<T,R> mapper)
Stream<R> flatMap(Function<T, Stream<R>> mapper)
mapToDouble, flatMapToDobule, mapToInt,…. 등 존재스트림의 요소를 변환 - 스트림의 최종연산 목록
최종연산 설명 void forEach(Consumer<? super T> action)
void forEachOrdered(Consumer<? super T> action)각 요소에 지정된 작업 수행 long count() 스트림 요소 개수 반환 Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)스트림의 최대값/최소값 반환 Optional<T> findAny() : 아무거나 하나
Optional<T> findFirst() : 첫번째 요소스트림의 요소를 하나 반환
* 병렬 스트림에서 사용 시 주의boolean allMatch(Predicate<T> p) : 모두만족 하는지
boolean anyMatch(Predicate<T> p) : 하나라도 만족 하는지
boolean noneMatch(Predicate<T> p) : 모두 만족하지 않는지주어진 조건을 모든 요소가 만족 시키는지,
만족 시키지 않는지 확인Object[] toArray()
A[] toArray(IntFunction<A[]> generator)스트림의 모든 요소를 배열로 반환 Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce (T identity, BinaryOperator<T> accumulator)
U reduce (U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner)스트림의 요소를 하나씩 줄여가면서 (리듀싱) 계산 R collect (Collector<T,A,R> collector)
R collect (Supplier<R> supplier, BiConsumer<R,T> accumulator, BiConsumer<R,R> combiner)스트림의 요소를 수집.
주로 요소를 그룹화하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용내용이 많아 스트림의 생성방법 / 중간연산-map(), flatMap() / Optional / 최종연산-reduce(), collect()는 이어서 작성
* 참고자료 : Java의 정석 | 남궁성
'DEV > JAVA' 카테고리의 다른 글
[JAVA] 쓰레드(Thread)의 동기화 / synchronized / Lock과 Condition / fork & join 프레임워크 (0) 2024.04.23 [JAVA] 쓰레드 (Thread)의 구현 / 싱글쓰레드, 멀티쓰레드 / 우선순위 / 쓰레드 그룹 / 데몬 쓰레드 (2) 2024.04.05 [JAVA] 제네릭스(Generics) / 열거형 (Enum) (2) 2024.04.03 [JAVA] 스트림(Stream) 생성 / 중간연산 map,flatMap / 최종연산 reduce, collect / Optional (2) (0) 2024.03.26 [JAVA] 람다식 (Lambda expression) (2) 2024.03.17