Lang/Java

스트림 요소 처리

soooy0 2024. 12. 26. 17:11
스트림이란?
  • 여태 컬렉션, 배열에 저장된 요소를 반복처리 하기 위해 for문을 사용하거나 iterator 반복자를 이용했다면, 또 다른 방법으로는 스트림을 사용할 수 있다.
  • 스트림은 요소들이 하나씩 흘러가면서 처리된다는 의미를 가지고 있다.
  • List 컬렉션에서 요소를 반복처리 하기 위해 스트림을 사용
// List의 stream()메소드로 Stream객체를 얻음
Stream<String> stream = list.stream();

// forEach메소드로 요소를 어떻게 처리할 지 람다식으로 제공한다.
stream.forEach( item -> // item 처리);

 

특징
  • 내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적이다.
  • 람다식으로 다양한 요소 처리를 정의할 수 있다.
  • 중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성할 수 있다.
내부/외부 반복자
1. 외부 반복자
  • for문과 iterator반복자는 컬렉션의 요소를 컬렉션 바깥쪽으로 반복해서 가져와 처리함
  • 컬렉션의 요소를 외부로 가져오는 코드와, 처리하는 코드를 모두 개발자 코드가 가지고 있어야 한다.

 

2. 내부 반복자
  • 스트림은 요소 처리방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리함
  • 개발자 코드에서 제공한 데이터 처리코드(람다식)을 가지고 컬렉션 내부에서 요소를 반복처리 한다.
  • 멀티코어 cpu를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있다.
  • 하나씩 처리하는 순차적 외부 반복자보다 효율적으로 요소를 반복시킬 수 있다.

 

중간 처리와 최종 처리

스트림은 하나 이상 연결될 수 있다. 컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림이 연결될 수 있고, 그 뒤에 매핑 중간 스트림이 연결그 다음 집계처리(최종처리)를 통해 결과를 도출힐 수 있다. 이렇게 스트림이 연결되어있는 것을 스트림 파이프라인이라고 한다.

 

:: 오리지널 스트림 --- 집계처리 사이의 중간 스트림들

  • 필터링 중간 스트림: 최종 처리를 위해 요소를 걸어냄
  • 매핑 중간 스트림: 요소를 변환 시킴
  • 최종 처리: 중간처리에서 정제된 요소들을 반복하거나 집계(카운팅, 총합, 평균)작업을 수행함

:: 예시

// Student 스트림
Stream<Student> studentStream = list.stream();

// student객체를 score로 매핑(변환)
// student 객체를 getScore() 메소드의 리턴값으로 매핑
InStream scoreStream = studentStream.mapToInt( student -> student.getScore() );

//평균 계산
double avg = scoreStream.average().getAsDouble();

 

mapToInt() 메소드는 객체를 int값으로 매핑해서 IntStream으로 변환시킨다. 어떤 객체를 어떤 int값으로 매핑할 것인지는 람다식으로 제공해야 한다. 따라서 student.getScore()을 사용하여 student 객체를 getScore()의 리턴값으로 매핑(변환)한다.

 

// 메소드 체이닝 패턴으로 간단하게 작성
double avg = list.stream()
	.mapToInt(student -> student.getScore())
	.average()
	.getAsDouble();

 

파이프라인 맨 끝에는 반드시 최종 처리 부분이 존재해야한다. 없으면 오리지널과 중간처리 스트림은 작동하지 않는다. average()이하를 생략하면 stream, mapToInt는 동작하지 않는 것임.

 

리소스로부터 스트림 얻기
  • java.util.stream 패키지에는 스트림 인터페이스들이 있다. BaseStream 인터페이스를 상위로 하는 하위 인터페이스들도 있다.
  • baseStream이 공통이고, 그 아래 Stream, IntStream 등등은 각각의 primitive타입들을 처리하는 스트림임.
  • java.util.Collection 인터페이스는 스트림과 parallelstream()메소드를 가지고 있기 때문에 자식 인터페이스인 List와 Set인터페이스를 구현한 모든 컬렉션에서 객체 스트림을 얻을 수 있다. 

 

배열로부터 스트림 얻기
숫자 범위로부터 스트림 얻기

IntStream 또는 LongStream의 정적 메소드인 range()와 rangeClosed()메소드를 이용하여 특정 범위의 정수 스트림을 얻을 수 있다.

첫번째 매개값은 시작수, 두번쨰 매개값은 끝수, 끝수를 포함하지 않으면 range(), 포함하면 rangeClosed()를 사용한다.

 

파일로부터 스트림 얻기

java.nio.Files의 lines() 메소드를 이용하면 텍스트 파일의 행 단위 스트림을 얻을 수 있다. 이는 텍스트 파일에서 한 행씩 읽고 처리할 때 사용할 수 있다. 

 

요소 걸러내기(필터링)

필터링은 요소를 걸러내는 중간처리 기능이다. 필터랑 메소드에는 distinct(), filter()가 있다.

- distinct() : 요소의 중복을 제거한다.

  • 객체 스트림일 경우, equals()메소드의 리턴갓이 true면, 동일한 요소로 판단한다. int, double, longStream은 같은 값일 경우 중복을 제거한다.
  • B A B A -> distinct()  -> B A

- filter() : 메소드는 매개값으루 주어진 predicate가 true를 리턴하는 요소만 필터링한다. 

  • C B A -> filter() :: A=true, B=false, C=true -> C A
  • Predicate는 함수형 인터페이스로, 매개값을 조사한 후 boolean을 리턴하는 test()메소드를 가지고 있다.
// Predicate<T> 람다식으로 표현하기

T -> {... return true}

or

T -> true; // return문만 있을 경우, 중괄호와 return키워드 생략 가능

 

요소 변환(매핑)
  • 매핑은 스트림의 요소를 다른 요소로 변환하는 중간처리 기능이다. 매핑 메소드는 mapXxx(), asDoubleStream(), asLongStream(), boxed(), floatMapXxx()등이 있다.

 

요소를 다른 요소로 변환
  • mapXxx()메소드는 요소를 다른 요소로 변환한 새로운 스트림을 리턴한다.
  • A B -> mapXxx() :: A > D, B > C -> D C
  • mapXxx()메소드의 종류가 있는데.. 매개타입으로 Function을 받는다. 이 매개타입은 함수형 인터페이스로, 각각의 추상메소드를 가지고 있다.
  • 모든 Function은 매개값을 리턴값으로 매핑(변환)하는 applyXxx()메소드를 가지고 있다.
// Function<T,R>을 람다식으로 표현

T -> {... return R; }

or

T -> R; // return문만 있을 경우 중괄호와 return키워드 생략 가능

 

요소를 복수 개의 요소로 변환
  • flatMapXxx()메소드 : 하나의 요소를 복수 개의 요소들로 변환한 새로운 스트림을 리턴한다.
  •  A B -> flatMapXxx() :: A -> A2, A1 / B-> B2, B1 -> A2 A1 B2 B1

 

요소 정렬
Comparable 구현 객체의 정렬
  • 스트림의 요소가 객체일 경우 객체가 comparable을 구현하고 있어야만 sorted()메소드를 사용하여 정렬할 수 있다. 그렇지 않으면 ClassCastException 발생
요소를 하나씩 처리(루핑)
루핑 메소드: peek(), forEach()

 

요소 조건 만족 여부(매칭)
매칭은 요소들이 ㅌ측정 조건에 만족하는지 여부를 조사하는 최종 처리 기능이다.
 요소 병렬 처리
동시성과 병렬성
  • 데이터 동시성: 멀티 스레드가 하나의 코어에서 번갈아가며 실행하는것
  • 데이터 병렬성: 멀티 스레드가 멀티 코어를 각각 이용해서 병렬로 실행하는 것을 말한다.
  • 동시성은 한 시점에서 하나의 작업만 실행하는데, 워낙 작업 실행 속도가 빨라서 동시에 처리되는 것처럼 보이는 것이다.
  • 병렬성은 한 시점에서 여러개의 작업을 병렬로 실행하기 때문에 동시성보다 좋은 성능을 낸다,

'Lang > Java' 카테고리의 다른 글

람다식  (1) 2024.12.17
스레드  (0) 2024.12.16
제네릭  (2) 2024.12.06
싱글톤  (0) 2024.12.05
형변환과 다형성  (0) 2024.12.04