Java

Stream

hyecozy 2022. 11. 8. 19:25

스트림이란?

- 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 값 요소

- 컬렉션의 요소를 하나씩 참조해 람다식으로 처리할 수 있는 반복자

더 쉽게 정의하자면 스트림은 데이터컬렉션 반복을 멋지게 처리하는 기능이다

 

왜 쓰는가?

- 원하는 값이 무엇인지 직관적으로 알 수 있는 코드를 짤 수 있게 해줌

- 자료구조가 포함하는 모든 값을 메소드에 포함하는 컬렉션과는 다르게 스트림은 요청할 때만 요소를 계산하는 고정된 자료구조를 가진다.

- (특정 연산자를 사용할 때) 여러 개의 조건이 중첩된 상황에서 값이 결정나면 불필요한 연산을 진행하지 않고 조건문을 빠져나와 실행 속도를 높인다.

 

어떻게 쓰는가?

1. 스트림 생성

Stream

2. 중간 연산 (n회 가능. 연산결과가 스트림인 연산)

Filter

Map

3. 최종 연산 (연산결과가 스트림이 아닌 연산. 스트림 요소를 소모하기 때문에 1회만 가능)

Collect

-> 이러한 과정을 파이프라이닝이라 부르기도 함

 

String[] strArr = {"dd", "aaa", "CC", "cc", "b"},
Stream<String> stream = Stream.of(strArr); //문자열 배열이 소스인 스트림
Stream<String> filteredStream = stream.filter(); //걸러내기 (중간연산)
Stream<String> distinctedSstream = stream.distinct();//중복제거 (중간연산)
Stream<String> sortedStream = stream.sort(); //정렬 (중간연산)
Stream<String> limitedStream = stream.limit(5); //스트림 자르기 (중간연산)
int total = stream.count(); //요소 개수 세기 (최종연산)

중간연산으로는 위와 같은 것들이 있다

 

스트림의 특징

1. 데이터 소스로부터 데이터를 읽기만 할 뿐 변경X (Read only )

List<Integer> list = Arrays.asList(3, 1, 5, 4, 2);
List<Integer> sortedList = list.stream().sorted()
							.collect(Collectors.toList());
System.out.println(list); // [3, 1, 5, 4, 2] list를 정렬해서 새로운 List인 sortedList에 저장
System.out.println(sortedList); // [1, 2, 3, 4, 5] //원본인 list는 그대로 있음

 

2. 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)

strStream.forEach(System.out::println); //모든 요소를 화면에 출력함 forEach라는 건 최종연산 (모든 요소를 소모함)
int numOfStr = strStream.count(); //에러. 스트림이 이미 닫혔음

 

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.println(str);

라는 코드가

stream.forEach(System.out::println);

이렇게 간결해진다 (성능은 비효율적임)

for문을 메소드 안에 감춰버린 거라볼 수 있음🔽

void forEach(Consumer<? super T> action) {
	Objects.requireNonNull(action); //매개변수의 널 체크
    
    for(T t : src)	//내부 반복 (for문을 메소드 안으로 넣음)
    	action.accept(T)
}

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

병렬 스트림이란, 멀티 쓰레드로 병렬 처리 하는 것

동시에 여러 개를 처리해서 빨리 결과를 얻을 수 있다는 것 (많은 데이터를 다룰 때 좋은)

 

Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
int sum = strStream.parallel() // 병렬 스트림으로 전환 (속성만 변경
		.mapToInt(s -> s.length()).sum(); //모든 문자열 길이의 합

스트림을 병렬 스트림으로 변환 할 때 위의 메소드를 쓰고 반대는 sequential()이 있다

 

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

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

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

 

 

 

참고

https://www.youtube.com/watch?v=wsvhgrCGW78 

https://www.youtube.com/watch?v=7Kyf4mMjbTQ