개발 일지/Java

[Java] 스트림(Stream)

미숫가루설탕많이 2023. 1. 6. 15:25

 자바에서는 파일이나 콘솔의 입출력을 직접 다루지 않고 스트림(Stream)이라는 흐름을 통해 다룬다. 여기서 스트림은 배열, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.

 

 스트림은 데이터 소스를 다루는 풍부한 메서드를 제공하며, 이를 통해 다량의 데이터에 복잡한 연산을 수행하면서도 가독성과 재사용성이 높은 코드를 작성할 수 있다.

 

 스트림은 다음과 같은 핵심적인 특징이 있다.

 

  • 스트림 처리 과정은 생성, 중간 연산, 최종 연산 세 단계의 파이프라인으로 구성된다.
    : 스트림 생성 -> 중간 연산 -> 최종 연산 -> 결과 리턴

  • 원본 데이터 소스를 변경하지 않는다.
    : 오직 데이터를 읽어올 수 있으며 데이터에 대한 변경과 처리는 생성된 스트림 안에서만 수행된다.

  • 일회용이다.
    : 최종 연산이 수행되고 난 후에는 스트림이 닫히고 다시 사용할 수 없기 때문에, 추가 작업이 필요하다면 다시 스트림을 생성해야 한다.

  • 내부 반복자이다.
    : 데이터 처리 코드만 컬렉션 내부로 주입하여 그 안에서 모든 데이터 처리가 이뤄지도록 한다.

  • 최종 연산 전까지 중간 연산을 수행하지 않는다.
    : 지연된 연산이라고도 한다.

 

 

 

 

스트림 파이프라인


스트림 생성

 스트림으로 데이터를 처리하기 위해서 가장 먼저 스트림을 생성해야 한다.

 

 스트림을 생성할 수 있는 데이터 소스는 배열, 컬렉션, 임의의 수 등 다양하며, 이에 따라 스트림의 생성 방법이 조금씩 차이가 있다.

// String 배열 스트림 생성 Arrays.stream()
String[] arr = new String[]{"C++", "자바", "파이썬"};
Stream<String> stream = Arrays.stream(arr);
stream.forEach(System.out::println);


// String 배열 스트림 생성 Stream.of()
String[] arr = new String[]{"C++", "자바", "파이썬"};
Stream<String> stream = Stream.of(arr);
stream.forEach(System.out::println);


// int 배열 스트림 생성
int[] intArr = {1,2,3,4,5};
IntStream intStream = Arrays.stream(intArr);
System.out.println("sum=" + intStream.sum());


// 컬렉션 스트림 생성
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();
stream.forEach(System.out::print);

 

 

 

 

스트림 중간 연산

 스트림의 중간 연산자의 결과는 스트림을 반환하기 때문에 여러 개의 연산자를 연결하여 원하는 데이터 처리를 수행할 수 있다.

 

  다양한 중간 연산자들이 있으며 자주 사용되는 중간 연산자로 filtering, maping, sorting 등이 있다.

 

  • filter()
    : Stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어 낸다. 매개값으로 조건(Predicate)이 주어지고, 조건이 참이 되는 요소만 필터링한다.

  • distinct()
    : Stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용한다.

  • map()
    : Stream 내 요소들에서 원하는 필드만 추출하거나 특정 형태로 변환할 때 사용한다.

  • sorted()
    : 괄호 안에 Comparator라는 인터페이스에 정의된 static 메서드와 default 메서드를 사용하여 정렬 작업을 수행한다. 괄오 안에 아무 값도 넣지 않은 상태로 호출하면 기본 정렬인 오름차순으로 정렬된다.

  • skip()
    : Stream의 일부 요소들을 건너뛴다.

  • limit()
    : Stream의 일부를 자른다.

  • peek()
    : forEach()와 같이 요소들을 순회하며 특정 작업을 수행한다. peek()는 중간 연산자이기 때문에 여러 번 연결해서 사용할 수 있지만 forEach()는 최종 연산자이기 때문에 마지막 단 한 번만 사용할 수 있다. 주로 디버깅 용도로 활용한다.

 

 

 

 

스트림 최종 연산

 중간 연산을 통해 변환된 스트림은 마지막으로 최종 연산을 통해 각 요소를 소모하여 결과를 표시한다. 즉, 모든 중간 연산들이 최종 연산 시 모두 수행된다.

 

 여기서 모든 요소를 소모한 스트림은 다시 사용할 수 없다.

 

  • 요소의 출력
    • forEach()

  • 요소의 소모
    • reduce()

  • 요소의 검색
    • findFrist()
    • findAny()

  • 요소의 검사
    • anyMatch()
    • allMatch()
    • noneMatch()

  • 요소의 통계
    • count()
    • max()
    • min()

  • 요소의 연산
    • sum()
    • average()

  • 요소의 수집
    • collect()

'개발 일지 > Java' 카테고리의 다른 글

[Java] 자바 가상머신(JVM)  (0) 2023.01.10
[Java] 스레드(thread)  (0) 2023.01.10
[Java] 람다식(Lambda Expression)  (0) 2023.01.06
[Java] 애너테이션(Annotation)  (0) 2023.01.06
[Java] 반복자(Iterator)  (0) 2023.01.04