컬렉션의 데이터를 표준화 하여 쉽게 다루기 위한 도구

컬렉션,배열 --> 1. 스트림 오픈 --> 2. 중간 연산 --> 3. 최종 연산
  • 스트림은 데이터를 읽기만 할 뿐 변경시키지 않음(Read Only)
  • 일회용 such as Iterator 
  • 내부 반복자를 통해 처리                cf) 외부 반복자 : Iterator
  • 최종 연산 전까지 중간 연산이 실행되지 않는다(lazy)

1. 스트림 오픈

  • ex) student.stream();

2. 중간 연산

  • 필터링, 매핑(변환), 정렬, 그룹핑, 루핑(순환)
  • 리턴타입 : 스트림

(1) 필터링

  • 지네릭이 바뀌지 않음

  • distinct() : 중복 제거
  • filter(Predicate predicate) : 필터링

(2) 변환

  • 지네릭이 바뀜                 

  • map(Function function) : 컬렉션의 element 맵핑
    • ex) map(Student::getName) : Student -> String
  • flatMap(Consumer consumer) : 컬렉션 내 컬렉션의 element 맵핑
    • ex) flatMap(Arrays::stream) : Array<String> -> String
String[][] arr = {
  {"a", "b", "c"}, 
  {"d", "e", "f"}
};

Arrays.stream(arr)
  .flatMap(Arrays::stream)
  .collect(Collectors.toList());

(3) 정렬

  • Comparable을 구현 한 클래스에서만 사용 가능, Comparable이 구현되지 않은 클래스는 예외 발생

  • sorted() : 기본 조건 정렬
  • sorted(Comparator comparator) : Comparator 조건 정렬

(4) 조회

  • peek() : 중간 처리 결과를 출력, 중간 작업결과 확인용으로 사용

3. 최종 연산

  • 스트림의 element를 소모해 결과 생성
  • 매칭, 집계(합계, 평균, 카운팅, 최대값, 최소값), 수집, 루핑
  • 리덕션 : 대량의 데이터를 가공해서 축소하는 것    ex) 합계, 평균, 카운팅, 최대값, 최소값
  • 메소드 : 리턴타입이 기본 타입 or Optional__
    → Optional__ : 타입이 래퍼 클래스임(언박싱이 필요함)

(1) 매칭

  • 요소들이 조건에 만족하는지 확인

  • __Match() : 조건이 만족하는지 확인
    • allMatch(), anyMatch(), noneMatch()
  • find__() : 조건일치 요소 반환
    • findFirst()
    • findAny() : 병렬 스트림에서 사용
public static void main(String[] args) {
    int[] intArr = {2, 4, 6};
		
    boolean result = Arrays.stream(intArr).allMatch(a -> a%2 == 0);
    System.out.println("모두 2의 배수인가 ? " + result);		// true
		
    result = Arrays.stream(intArr).anyMatch(a -> a%3 == 0);
    System.out.println("하나라도 3의 배수인가 ? " + result);	// true
		
    result = Arrays.stream(intArr).noneMatch(a -> a%3 == 0);
    System.out.println("3의 배수는 없는가 ? " + result);		// false
}

(2) 집계

  • sum(), average(), count(), max(), min()
  • reduce() : 커스텀 합계
    • 요소를 하나씩 줄여나가며 계산

(3) 수집

  • 주로 요소를 그룹화하거나 분활한 결과를 컬렉션에 담아 반환할 때 사용

  • collect(Collector collector)
  • Collectors : 이미 구현되어 있는 Collector 인터페이스 구현체
    • toList(), toSet(), toMap(key, value), toCollection(), toArray()
  • groupingBy(Function) / partitioningBy(Predicate) : 스트림 ->Map
    • groupingBy(key, value) & partitioningBy(key, value) 라고 생각
// 1. 단순분할(성별)
Map<Boolean, List<Student2>> stuBySex = Stream.of(stuArr)
	.collect(Collectors.partitioningBy(Student2::isMale));
List<Student2> maleStudent = stuBySex.get(true);
List<Student2> femaleStudent = stuBySex.get(false);

// 2. 단순분할 + 통계(성별 학생수)
Map<Boolean, Long> stuNumBySex = Stream.of(stuArr)
	.collect(Collectors.partitioningBy(Student2::isMale, Collectors.counting()));
System.out.println("남학생 수 : " + stuNumBySex.get(true));
System.out.println("여학생 수 : " + stuNumBySex.get(false));

// 3. 단순분할 + 통계(성별 1등)
Map<Boolean, Optional<Student2>> topScoreBySex = Stream.of(stuArr)
	.collect(Collectors.partitioningBy(Student2::isMale, Collectors.maxBy(Comparator.comparingInt(Student2::getScore))));
System.out.println("남학생 수 : " + topScoreBySex.get(true));
System.out.println("여학생 수 : " + topScoreBySex.get(false));

// 4. 다중분할(성별, 불합격자)
Map<Boolean, Map<Boolean, List<Student2>>> failedStuBySex = Stream.of(stuArr)
	.collect(Collectors.partitioningBy(Student2::isMale, Collectors.partitioningBy(s -> s.getScore() <= 100)));
List<Student2> failedMaleStu = failedStuBySex.get(true).get(true);	// 1번get: 성별, 2번get: score<=100
List<Student2> failedFemaleStu = failedStuBySex.get(false).get(true);

○ collect(supplier, accumulator, combiner) 구현

  • supplier - Supplier : 사용자정의 컨테이너 생성
  • accumulator - BiConsumer<R, ? super T> : 스트림의 요소를 수집
  • combiner - BiConsumer<R, R> : 병렬 스트림의 경우 두 컨테이너를 병합

(4) 루핑

  • forEach(Consumer consumer)
double avg = list.stream()                 // 오리지널 스트림 (Collections -> Stream)
         .mapToInt(s -> s.getScore())      // 중간 처리 (Stream -> IntStream)
         .average()                        // 최종 처리 (IntStream -> OptionalDouble)
         .getAsDouble();                   // 언박싱(Wrapper -> 기본(OptionalDouble -> double))
		
System.out.println("평균 점수 : " + avg);

○ 병렬 처리 : parallelStream()

  • 내부적으로 쓰레드를 사용해서 병렬로 처리함

● 스트림의 변환

스트림기본형 스트림 StreamIntStream mapToInt(Function)
기본형 스트림스트림
IntStreamStream boxed()
기본형 스트림기본형 스트림 IntStreamDoubleStream asDoubleStream()
2개 스트림스트림 Stream, Stream Stream concat(Stream a, Stream b)
스트림의 스트림스트림 StreamStream flatMapTo__(Function)
스트림↔병렬스트림   parallel() : 스트림병렬스트림
sequential() : 스트림병렬스트림
스트림컬렉션(List,Set) Stream→Collection,List,Set collect(Collectors.to___())
컬렉션→스트림 Collection,List,SetStream stream()
스트림→컬렉션(Map) Stream→Map collect(Collectors.toMap(function))
스트림→배열 Stream->Object[] toArray()