[Java] Stream 스트림
배열, 컬렉션의 저장 요소를 하나씩 참조해 람다식으로 처리할 수 있도록 하는 반복자
List, Set, Map 등 다양한 데이터 소스로부터 스트림을 만들 수 있음
// 명령형 방식
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum = 0;
for(int number : numbers){
    if(number > 4 && (number % 2 == 0)){
        sum += number;
    }
}
// 선언형 방식
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum =
        numbers.stream()
                .filter(number -> number > 4 && (number % 2 == 0))
                .mapToInt(number -> number)
                .sum();
배열 스트림 생성
Arrays 클래스의 stream() 메서드 또는 Stream클래스의 of() 메서드를 사용할 수 있다.
Arrays.stream()
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Arrays.stream(arr);
// 출력
stream.forEach(System.out::println);
Stream.of()
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Stream.of(arr);
// 출력
stream.forEach(System.out::println);
배열로 스트림을 생성할 시에는 Arrays.stream()와 Stream.of() 둘 중 더 편한 메서드를 선택하여 사용할 수 있다.
컬렉션 스트림 생성
List, Set 등의 경우, stream() 메서드를 사용하여 스트림을 생성할 수 있다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Stream<Integer> stream = list.stream();
stream.forEach(System.out::print);
중간 연산

필터링
distinct()중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용한다.filter()조건에 맞는 데이터만 정제하여 더 작은 컬렉션을 만든다.
List<String> names = Arrays.asList("가가", "나나", "다다", "가가", "잉잉");
names.stream()
        .distinct() //중복제거
        .filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링 
        .forEach(element -> System.out.println(element));
매핑
원하는 필드만 추출하거나 특정 형태로 변환할 때 사용한다.
**.map()**
List<String> names = Arrays.asList("aa", "bb", "cc");
names.stream()
        .map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
        .forEach(element->System.out.println(element));
List<Integer> list = Arrays.asList(1, 3, 6, 9);
// 각 요소에 3을 곱한 값을 반환
list.stream().map(number -> number * 3).forEach(System.out::println);
**.flatMap()**
중첩 구조를 제거하고 단일 컬렉션으로 만들어주는 역할을 한다.
// map() 사용
        Arrays.stream(namesArray)
                .map(inner -> Arrays.stream(inner))
                .forEach(System.out::println);
// 출력값
java.util.stream.ReferencePipeline$Head@3cb5cdba
java.util.stream.ReferencePipeline$Head@56cbfb61
// flatMap()
Arrays.stream(namesArray).flatMap(Arrays::stream).forEach(System.out::println);
// 출력값
aa
bb
cc
정렬
정렬할 때 사용한다.
List<String> animals = Arrays.asList("T", "L", "M", "D", "H", "C");
animals.stream().sorted().forEach(System.out::println);
// 출력값
C
D
H
L
M
T
역순으로 정렬
List<String> animals = Arrays.asList("T", "L", "M", "D", "H", "C");
animals.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
// 출력값
T
M
L
H
D
C
기타
skip()- 스트림의 일부 요소들을 건너뛴다.limit()- 스트림의 일부를 자른다.- 
    
peek()-forEach()와 마찬가지로, 요소들을 순회하며 특정 작업을 수행한다peek()는 중간 연산자이기 때문에 여러번 연결하여 사용할 수 있는 반면,forEach()는 최종 연산자이기 때문에 한번만 사용 가능하다. 
스트림 최종 연산
기본 집계
sum(), count(), average(), max(), min()
int[] intArray = {1,2,3,4,5};
// 배열의 첫 번째 요소 
int first = Arrays.stream(intArray).findFirst().getAsInt(); // 1
// 최소값
int min = Arrays.stream(intArray).min().getAsInt(); // 1
// 최대값
int max = Arrays.stream(intArray).max().getAsInt(); // 5
// 합계
long sum = Arrays.stream(intArray).sum(); // 15
// 평균
double average = Arrays.stream(intArray).average().getAsDouble(); // 3.0
// 카운팅
long count = Arrays.stream(intArray).count(); // 5
최종 연산자 뒤에 getAsInt(), getAsDouble() 메서드가 있는 이유
OptionalDouble average = Arrays.stream(intArr).average(); // OptionalDouble[3.0]
double result = average.getAsDouble(); // 3.0
매칭
allMatch(), anyMatch(), noneMAtch()
allMatch()- 모든 요소들이 조건을 만족하는 지 여부를 판단한다.noneMatch()- 모든 요소들이 조건을 만족하지 않는 지 여부를 판단한다.anyMatch()- 하나라도 조건을 만족하는 요소가 있는 지 여부를 판단한.
int[] intArray = {2,4,6};
boolean result = Arrays.stream(intArray).allMatch(element-> element % 2 == 0);
//true
result = Arrays.stream(intArray).anyMatch(element-> element % 3 == 0);
// true
 result = Arrays.stream(intArray).noneMatch(element -> element % 3 == 0);
 //false
요소 소모
스트림의 요소를 줄여나가면서 연산을 수행하고 최종적인 결과를 반환한다.
**reduce()**
int[] intArray = {1,2,3,4,5};
int sum1 = Arrays.stream(intArray)
        .map(element -> element * 2)
            .reduce((a , b) -> a + b)
        .getAsInt();
// 30
- .map(각 요소에 2를 곱해줌)
 - .reduce((a , b) -> a + b) (a: 누적 값, b: 새로 더해질 값)
 - 2+4 → a: 6, b: 6
 - 6+6 → a:12, b: 8
 - 12+8 → a:20, b: 10
 - 20+10 → 최종 결과 30
 
요소 수집
- 
    
**[collect()](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html)** https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.htmlCollector 인터페이스 타입의 인자를 받아서 처리 한다.
collect() 메서드는 요소를 수집하는 기능 외에도 요소 그룹핑 및 분할 등 다른 기능들을 제공한다.
 
public class TerminalOperationExample {
    public static void main(String[] args) {
        // Student 객체로 구성된 배열 리스트 생성 
        List<Student> totalList = Arrays.asList(
                new Student("김코딩", 100, Student.Gender.Male),
                new Student("박해커", 80, Student.Gender.Male),
                new Student("이자바", 90, Student.Gender.Female),
                new Student("나미녀", 60, Student.Gender.Female)
        );
        
        // 스트림 연산 결과를 Map으로 반환
        Map<String, Integer> maleMap = totalList.stream()
                .filter(s -> s.getGender() == Student.Gender.Male)
                .collect(Collectors.toMap(
                        student -> student.getName(), // Key
                        student -> student.getScore() // Value
                ));
        // 출력
        System.out.println(maleMap);
    }
}
class Student {
    public enum Gender {Male, Female};
    private String name;
    private int score;
    private Gender gender;
    public Student(String name, int score, Gender gender) {
        this.name = name;
        this.score = score;
        this.gender = gender;
    }
    public String getName() {
        return name;
    }
    public int getScore() {
        return score;
    }
    public Gender getGender() {
        return gender;
    }
}
// 출력값
{김코딩=100, 박해커=80}
댓글남기기