본문 바로가기

Java

Java 스트림(stream)

 

- 다양한 데이터 소스(컬렉션, 배열, 람다식, 난수 스트림 등)를 표준화된 방법으로 다루기 위한 것

- 스트림 만들기 -> 중간연산(0~n 번) -> 최종연산(1번)으로 결과를 얻음

 

 

1. 스트림의 특징

 

1) 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않음

- 필요시 정렬된 결과를 컬렉션이나 배열에 담아 반환 가능

 

2) Iterator처럼 일회용(필요한 경우 다시 스트림 생성해야 함)

 

3) 최종 연산 전까지 중간연산이 수행되지 않음 - 지연된 연산

IntStream intStream = new Random().ints(1, 46); // 1~ 45 범위의 무한 스트림
IntStream.distinct().limit(6).sorted() // 중간연산, 수행되지 않고 수행해야할 내용을 지정하는 것
		    .forEach(i -> System.out.print(i+",")); // 최종연산

 

 

4) 작업을 내부 반복으로 처리 

// 반복문
for(String str : strList)
	System,out,prinln(str);
    
// forEach()메서드는 스트림에 정의된 메서드로, 매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용함
stream.forEach(System.out::prinln);

 

 

5) 작업을 병렬로 처리 - 병렬 스트림

- parallel() 메서드로 병렬 스트림으로 전환

 

6) 기본형 스트림 - IntStream, LongStream, DoubleStream

- 오토박싱&언박싱의 비효율이 제거됨(Stream <Integer> 대신 IntStream 사용)

- 숫자와 관련된 유효한 메서드를 Stream <T>보다 더 많이 제공

 

 

 

2. 스트림 만들기

 

1) 컬렉션을 스트림으로 만들기

- 컬렉션의 최고 조상인 Collection에 stream()이 정의되어 있음

- List와 Set으로 구현한 컬렉션은 stream() 메서드로 스트림 생성

// Collection 인터페이스의 stream()메서드를 이용하여 생성
Stream<E> stream()

 

 

2) 배열을 스트림으로 만들기

- Stream과 Arrays에 static 메서드로 정의되어 있음

Stream<T> Stream.of(T... values)
Stream<T> Stream.of(T[])
Stream<T> Arrays.Stream(T[])
Stream<T> Arrays.Stream(T[] array, int startInclusive, int endExlusive)

 

- 기본형 배열을 소스로 하는 스트림 생성 가능

 

3) 임의의 수(난수)를 갖는 스트림 생성

- Random클래스의 메서드 이용하여 임의의 수를 요소로 갖는 (무한) 스트림 생성 

// 스트림의 크기가 정해지지 않은 무한 스트림(infinite stream) 반환하는 메서드
IntStream ints()
LongStream longs()
DoubleStream doubles()

 

- 스트림의 크기를 제한하여 유한 스트림 생성

// 매개변수로 스트림의 크기를 지정하여 유한 스트림(tream) 반환하는 메서드
IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)

 

 

4) 특정 범위의 정수를 요소로 갖는 스트림 생성

IntStream IntStream.range(int begin, int end) // end 포함하지 않음
IntStream IntStream.rangeClosed(int begin, int end) // end 포함

 

 

5) 람다식을 이용한 스트림 생성

// 지정된 seed값을 시작으로 람다식 f에 의해 계산된 결과를 다시 seed값으로 하여 계산 반복하여 무한 스트림 생성(이전 요소에 종속적)
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

// seed값 없이 람다식에 의해 계산되는 값을 요소로하는 무한 스트림 생성(이전 요소에 독립적)
static <T> Stream<T> generate(Supplier<T> s)

 

 

6) 파일을 소스로 하는 스트림 생성

- java.nio.file.Files는 파일을 다루는데 필요한 메서드 제공

 

// list()는 지정된 디렉토리(dir)에 있는 파일의 목록을 소스로하는 스트림을 생성하여 반환
Stream<Path> Files.list(Path dir) // Path는 파일 또는 디렉토리

// lines()는 파일의 한 라인을 요소로 하는 스트림을 생성하여 반환
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)

// BufferedReader 클래스의 lines()메서드는 파일 뿐만 아니라 다른 입력 대상으로부터 데이터를 행단위로 읽어옴
Stream<String> lines()

 

 

7) 비어있는 스트림 생성

// empty()는 빈 스트림을 생성해서 반환
Stream emptyStream = Stream.empty();

 

 

 

3. 중간연산(0 ~ n 번)

- 연산결과가 스트림인 연산

- 반복적으로 적용 가능

 

1) 자르기 - skip(), limit()

// 앞에서부터 n개 건너뛰기
Stream<T> skip(long n)

// maxSize 이후의 요소 잘라내기
Stream<T> limit(long maxSize)

 

 

2) 요소 걸러내기 - filter(), distinct()

// 조건에 맞지 않는 요소 제거
Stream<T> filter(Predicate<? super T> predicate)

// 중복 제거
Stream<T> distinct()

 

 

3) 정렬 - sorted()

// 스트림 요소의 기본정렬(Comparable)로 정렬 
Stream<T>

// 지정된 Comparator로 정렬
Stream<T> sorted(Comparator<? super T> comparator)

 

 

4) 정렬 - Comparator의 메서드 comparing(), thenComparing()

// comparing()메서드로 정렬기준 지정 
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)

// 비교대상이 기본형인 경우
comparingInt(ToIntFuction<T> keyExtractor)
comparingLong(ToLongFunction<T> keyExtractor)
comparingDouble(ToDoubleFunction<T> keyExtractor)


// 정렬 조건 추가
thenComparing(Comparator<T> other)
thenComparing(Comparator<T, U> keyExtractor)
thenComparing(Comparator<T, U> keyExtractor, Comparator<U> keyComp)

 

 

5) 변환 - 원하는 필드만 뽑거나 특정 형태로 변환하는 map()

Stream<R> map(Function<? super T, ? extends R> mapper)

 

 

6)  스트림 요소를 소비하지 않고 엿보기(중간 작업 결과 확인) - peek()

// 중간 연산(스트림 요소를 소비하지 않음)
Stream<T> peek(Consumer<? super T> action)

 

 

7) 변환 - 스트림의 스트림을 스트림으로 변환하는 flatMap()

Stream<String> strStrStrm = strArrStrim.flatMap(Arrays::stream);

 

 

 

4. 최종연산(1번)

- 연산결과가 스트림이 아닌 연산

- 단 한 번만 적용 가능(스트림의 요소를 소모)

 

1) 스트림의 모든 요소에 지정된 작업 수행 - forEach(), forEachOrdered()

// 병렬 스트림인 경우 순서가 보장되지 않음
void forEach(Consumer<? super T> action)

// 병렬 스트림인 경우에도 순서가 보장됨
void forEachOrdered(Consumer<? super T> action)


// 직렬 스트림
IntStream.range(1, 10).sequential().forEach(System.out::print); // 순서가 보장됨
IntStream.range(1, 10).sequential().forEachOrdered(System.out::print); // 순서가 보장됨

// 병렬 스트림
IntStream.range(1, 10).parallel().forEach(System.out::print);  // 순서가 보장되지 않음
IntStream.range(1, 10).parallel().forEachOrdered(System.out::print); // 순서가 보장됨

 

 

2) 조건 검사 - allMatch(), anyMatch(), noneMatch()

boolean allMatch(Predicate<? super T> predicate) // 모든 요소가 조건 만족시키면 true
boolean anyMatch(Predicate<? super T> predicate) // 한 요소라도 조건 만족시키면 true
boolean noneMatch(Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키지 않으면 true

 

 

3) 조건에 일치하는 요소 찾기 -  findFirst(), findAny()

Optional<T> findFirst() // 첫 번째 요소 반환, 순차 스트림에 사용
Optional<T> findAny() // 조건에 일치하는 요소 아무거나 하나 반환, 병렬 스트림에 사용

 

 

4) 스트림의 요소를 하나씩 줄여가며 누적연산 수행 - reduce()

Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<T> combiner)

 

 

5) 스트림의 최종연산 - collect()

① collector() 최종연산

- Collector를 매개변수로 하는 스트림의 최종 연산

👉🏻  매개변수가 Collector를 구현한 클래스의 객체여야 함

Object colelct(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로 지정
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

 

 

② Collector 인터페이스

- collector()가 스트림의 요소를 수집하기 위한 방법(메서드)을 정의해 놓은 인터페이스 

- 직접 구현해서 사용할 수 있지만 Collectors클래스에 다양한 기능을 구현되어 있어서 Collectors클래스만으로도 많은 일을 할 수 있음

 

③ Collectors 클래스

- Collector 인터페이스를 구현한 Collectors 클래스

- static 메서드로 미리 작성된 컬렉터 제공

분류 메서드 설명
스트림을 컬렉션으로 변환 toList() 스트림을 List로 변환
toSet() 스트림을 Set으로 변환
toMap() 스트림을 Map으로 변환
toCollection() 스트림을 원하는 컬렉션의 생성자 참조를 매개변수로 넣어 원하는 컬렉션으로 변환
스트림을 배열로 변환 toArray() 스트림을 배열로 변환
스트림의 통계 counting() 스트림 요소를 그룹별로 카운트(count()는 전체만 카운트 할 수 있음)
summingInt() 스트림 요소의 합계 반환(sum()은 전체를 더함)
maxBy() 스트림 요소 중 최대값 반환
minBy() 스트림 요소 중 최소값 반환
averageingInt() 스트림 요소의 평균 값 반환
스트림을 리듀싱 reducing() 스트림 그룹별 리듀싱(reduce()는 전체 리듀싱)
스트림을 문자열로 결합 joining() 문자열 스트림의 요소를 모든 연결 
스트림의 그룹화 groupingBy() 스트림의 요소를 특정 기준으로 그룹화(n분할)
스트림의 분할 partitioningBy() - 스트림을 조건에 일치하는 것과 일치하지 않는 것으로 그룹화(2분할)
- 이중 분할 가능

 

'Java' 카테고리의 다른 글

Java 입출력(I/O)  (0) 2023.01.26
Java Optional<T>  (0) 2023.01.20
Java 람다(Lambda)  (0) 2023.01.17
Java 스레드(thread)  (0) 2023.01.17
Java 애너테이션(annotation)이란?  (0) 2023.01.17