자바 - 스트림(Stream)

SeungTaek·2021년 8월 24일
4

자바(Java)

목록 보기
4/8
post-thumbnail

본 게시물은 스스로의 공부를 위한 글입니다.
틀린 내용이 있을 수 있습니다.

📒 스트림(Stream)

  • 다양한 데이터 소스(컬렉션, 배열 등)를 표준화된 방법으로 다루기 위한 것
    • 그 전까지는 List, Set, Map 등의 사용 방법들이 서로 달랐다.
  • 데이터 소스를 스트림으로 변환 후 여러 번의 중간연산과 마지막의 최종 연산을 통해 다를 수 있다.
  • 순서
    1. 스트림 만들기
    2. 중간연산(반복 적용 가능, 연산 결과가 스트림)
    3. 최종연산 (스트림의 요소를 소모) -> 결과 리턴
list.stream() //스트림 만들기
	.distinct() //중간연산
    .limit(5) //중간연산
    .sorted() //중간연산
    .forEach(System.out::println) //최종연산

📒 스트림의 특징

  1. 데이터를 담고 있는 저장소 (컬렉션)이 아니다.
  2. 스트림은 원본 데이터 소스를 변경하지 않는다.(Read Only)
  3. 스트림은 lterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)
  4. 최종 연산 전까지 중간연산을 수행되지 않는다.(lazy)
  5. 무제한일 수도 있다. (Short Circuit 메소드를 사용해서 제한할 수 있다.)
  6. 손쉽게 병렬 처리할 수 있다. (멀티 쓰레드 사용) (.parallel)
  7. 기본형 스트림으로 IntStream, LongStream, DoubleStream등 제공
    • 오토박싱 등의 불필요한 과정이 생략됨.
    • Stream<Integer> 대신에 IntStream을 사용하는게 더 효율적이다.
    • 뿐만 아니라 숫자의 경우 더 유용한 메서드를 Stream<T>보다 더 많이 제공한다.(.sum(), .averge() 등)

📒 스트림 사용하기

📌 1. 스트림 생성

  • Collection의 경우 .stream()으로 스트림 변환
Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5).stream();
  • 객체 배열로부터 스트림 생성
Stream.of("a", "b", "c");
Stream.of(new String[]{"a", "b", "c"});
Arrays.stream(new String[]{"a", "b", "c"});
Arrays.stream(new String[]{"a", "b", "c"}, startIndex, EndIndex+1);
  • 람다식 iterate(), generate()
//Stream.iterate(T seed, UnaryOperator f); 이전 결과에 종속적
Stream.iterate(0, n->n+2); //0, 2, 4, ... 무한 스트림

//generate는 초기값 지정x, 이전 결과와 무관하다.
Stream.generate(Math::random);
Stream.generate(()->1);

📌 2. 스트림의 중간 연산

.distinct() //중복제거
.filter(Predicate<T> predicate) //조건에 안 맞는 요소는 제외
.limit(long maxSize) //maxSize 이후의 요소는 잘래냄
.skip(long n) //앞에서부터 n개 건너뛰기
.sorted() //기본 정렬로 정렬
.sorted(Comparator<T> comparator) //조건에 맞게 요소 정렬. 추가 정렬 기준을 제공할 때는 thenComparing()사용
    
 //스트림의 요소를 변환. ex) map(File::getName), map(s->s.subString(3))
.map(Function<T> mapper) 
    
 //요소에 작업수행. 보통 중간 작업결과 확인으로 사용. peek(s->System.out.println(s))
.peek(Consumer<T> action)
    
 //스트림의 스트림을 스트림으로 변환
 //ex) Stream<String> strStrm=strArrStrm.flatMap(Arrays::stream)
.flatMap()

📌 3. 스트림의 최종 연산

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) 

//데이터를 변형 등의 처리를 하고 원하는 자료형으로 변환해 줍니다.
//아래에서 자세히 보자.
collect( ~ )

📒 Stream API 사용 예시

🎈 Filter(Predicate)

  • if문이라고 생각하면 될듯.

  • 람다식의 리턴값은 boolean. true면 다음 단계 진행, false면 버려짐

classes.stream()
	.filter(c->c.getTitle().startWith("spring"))
	.forEach(c->System.out.println(oc.getId));

classes.stream()
	.filter(Predicate.not(OnlineClass::isClosed))
	.forEach(c->System.out.println(oc.getId));
  • 예) 이름이 3글자 이상인 데이터만 새로운 스트림으로 변경하기

🎈 Map(Function) 또는 FlatMap(Function)

  • stream을 우리가 원하는 모양의 새로운 스트림으로 변환

    • 예) 각각의 File에서 String name만 새로운 스트림으로
    • 예) string 타입 요소를 짤라서 새로운 스트림으로
 map(File::getName)
 map(s->s.subString(3))

🎈 limit(long) 또는 skip(long)

  • 예) 최대 5개의 요소가 담긴 스트림을 리턴한다.
  • 예) 앞에서 3개를 뺀 나머지 스트림을 리턴한다.
Stream.iterate(10, i->i+1)
		.skip(10)
        .limit(10)
        .forEach(System.out::println)

🎈 anyMatch(), allMatch(), nonMatch()

  • 예) k를 포함한 문자열이 있는지 확인한다. (true 또는 false를 리턴한다.)
boolean test=javaClasses.stream()
				.anyMatch(oc->oc.getTitle().contains("k"));
  • 예) 스트림에 있는 모든 값이 10보다 작은지 확인한다.

🎈 findFirst() VS findAny()

  • 스트림을 직렬로 처리할 때는 차이가 없다.

  • 하지만 병렬로 처리할 경우에 차이가 생기는데,

    • findFirst()는 stream의 순서를 고려해, 가장 앞쪽에 있는 요소를 반환
    • findAny()는 멀티 쓰레드에서 가장 먼저 찾은 요소를 반환. stream의 뒤쪽에 있는 요소가 반환될 수도 있다.

🎈 reduce 자세히 보기

  • 스트림의 요소를 하나씩 줄여가며 누적연산 수행
  • reduce(초기값, (누적 변수, 요소)-> 수행문)
Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
					.reduce((total, y) -> total + y);
System.out.println("sum: " + s); //sum: 55

//초기값을 지정해 줄 수 있다.
Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
					.reduce(10, (total, n) -> total + n);
System.out.println("sum: " + sum); //sum: 65

🎈 collect 자세히 보기

  • Stream의 요소들을 우리가 원하는 자료형으로 변환할 수 있다.
stream.collect(Collectors.toSet()); //set으로 변환
stream.collect(Collectors.toList()); //list 변환
stream.collect(Collectors.joining()); //한개의 string으로 변환
stream.collect(Collectors.joining(", ")); //요소들 사이에 ","을 넣어서 한개의 string 반환

Reference

자바의 정석
인프런 더 자바(백기선)

profile
I Think So!

1개의 댓글

comment-user-thumbnail
2023년 8월 7일

잘 보고 갑니다!!

답글 달기