본문 바로가기

Java

Java Stream(스트림)은 무엇일까?

728x90
반응형

Java Stream?

자바의 Stream은 Java 8에서 도입된 기능으로, 데이터의 컬렉션을 처리하고 조작하는 데 유용하다.

Stream을 사용하면 데이터 처리 파이프라인을 구축할 수 있으며, 이는 코드의 가독성을 높이고 유지 보수를 하는데 도움을 준다.


Stream의 특징을 알아보자

1. 선언형 프로그래밍이 가능하다
    - filter(), map(), collect()와 같은 메서드를 체이닝하여 사용할 수 있다. 
    - 명령형 프로그래밍 방식인 '어떻게'가 아닌 '무엇을' 할지를 표현한다. 
    ex) 상사가 부하직원에게 회원조회 API를 개발하라는 업무를 지시하는 상황 
    명령형 : OO님, 회원의 아이디 값을 요청받아서 DB Select SQL을 날려서 회원 정보를 추출한 다음 User라는 객체에 정보를 담아서 return 해주는 방법으로 API를 만들어주세요.
    선언형 : OO님, 회원 정보를 return 해주는 API를 만들어주세요. 


    명령형은 '어떻게'라는 방식이 고정되어 있기 때문에 자율성이 떨어진다.
    그러나 선언형의 경우 return 해주는 정보는 같지만 그 정보를 만드는 과정에 자율성이 있다는 점에서 재사용성이 좋다        고 볼 수 있다.  

 

2. Stream은 저장소가 아니다.

    - 스트림 자체는 데이터 저장소가 아니고 데이터 소스로부터 데이터를 읽고 처리하는 역할만 한다. 

    

3. Stream은 데이터 소스에서 추출된 요소에서 시퀀스를 만들고 처리한다.

    - 순차적으로 처리한다. Like for문(?)

 

4. Stream은 일회성이다. 

    - 최종 연산을 처리하면 Stream은 소멸된다. 

    - 다시 사용하려면 새로운 Stream을 생성해야 한다.

 

5. Stream은 병렬처리를 지원한다. 


6. Stream은 중간연산과 최종연산이 있다. 

    - 중간 연산 (Intermediate Operations): 스트림을 변환하지만 새로운 스트림을 반환한다. 무한히 체이닝이 가능하다.

  • 예: filter, map, flatMap, sorted, distinct, limit, skip

    - 최종 연산 (Terminal Operations): 스트림을 닫고 결과를 반환하거나 부수 효과를 일으킵니다.

  • 예: forEach, collect, reduce, count, findFirst, findAny, anyMatch, allMatch, noneMatch

중간연산

1. filter

  • 설명: 스트림의 각 요소에 대해 주어진 조건(predicate)을 검사하고, 조건을 만족하는 요소만을 포함하는 새로운 스트림을 반환한다.
  • 용도: 특정 조건에 맞는 요소들을 필터링할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> result = list.stream()
                          .filter(s -> s.startsWith("a"))
                          .collect(Collectors.toList());
// result: ["apple"]

 

2. map

  • 설명: 스트림의 각 요소에 주어진 함수(function)를 적용하여 새로운 요소로 변환하고, 변환된 요소들로 구성된 새로운 스트림을 반환한다.
  • 용도: 요소들을 변환할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<Integer> result = list.stream()
                           .map(String::length)
                           .collect(Collectors.toList());
// result: [5, 6, 6]

 

3. flatMap

  • 설명: 스트림의 각 요소에 주어진 함수(function)를 적용하여 스트림을 생성하고, 생성된 여러 스트림을 하나의 스트림으로 평면화한다.
  • 용도: 중첩된 구조를 평면화할 때 사용한다.
  • 예제:
List<List<String>> list = Arrays.asList(
    Arrays.asList("apple", "banana"),
    Arrays.asList("cherry", "date")
);
List<String> result = list.stream()
                          .flatMap(Collection::stream)
                          .collect(Collectors.toList());
// result: ["apple", "banana", "cherry", "date"]

4. sorted

  • 설명: 스트림의 요소들을 자연 순서(오름차순)나 제공된 비교자(comparator)에 따라 정렬하여 새로운 스트림을 반환한다.
  • 용도: 요소들을 정렬할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("cherry", "banana", "apple");
List<String> result = list.stream()
                          .sorted()
                          .collect(Collectors.toList());
// result: ["apple", "banana", "cherry"]

5. distinct

  • 설명: 스트림의 요소들 중 중복된 요소를 제거하여 고유한 요소들로 구성된 새로운 스트림을 반환한다.
  • 용도: 중복을 제거할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "apple", "cherry");
List<String> result = list.stream()
                          .distinct()
                          .collect(Collectors.toList());
// result: ["apple", "banana", "cherry"]

6. limit

  • 설명: 스트림의 처음 n개의 요소들만 포함하는 새로운 스트림을 반환한다.
  • 용도: 요소의 개수를 제한할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = list.stream()
                          .limit(2)
                          .collect(Collectors.toList());
// result: ["apple", "banana"]

7. skip

  • 설명: 스트림의 처음 n개의 요소들을 건너뛰고 나머지 요소들로 구성된 새로운 스트림을 반환한다.
  • 용도: 특정 개수의 요소를 건너뛸 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = list.stream()
                          .skip(2)
                          .collect(Collectors.toList());
// result: ["cherry", "date"]

최종연산

1. forEach

  • 설명: 스트림의 각 요소에 대해 주어진 동작을 수행합니다. 이 메서드는 스트림의 요소를 반복(iterate)하는 데 사용된다.
  • 용도: 요소들에 대해 부수 효과(side-effect)를 일으키는 작업을 수행할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream().forEach(System.out::println);
// 출력: apple, banana, cherry (각 요소가 개별적으로 출력됨)

 

2. collect

  • 설명: 스트림의 요소들을 수집하여 리스트, 세트, 맵 등의 다른 형태로 변환합니다. 주로 Collectors 유틸리티 클래스를 사용한다.
  • 용도: 스트림의 결과를 특정 컬렉션으로 변환할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> result = list.stream().collect(Collectors.toList());
// result: ["apple", "banana", "cherry"]

3. reduce

  • 설명: 스트림의 요소들을 결합하여 하나의 결과를 생성한다. 초기값과 결합 함수를 인수로 받는다.
  • 용도: 스트림의 모든 요소를 결합하여 단일 결과를 도출할 때 사용한다.
  • 예제:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, Integer::sum);
// sum: 15

4. count

  • 설명: 스트림의 요소 개수를 반환한다.
  • 용도: 스트림에 포함된 요소의 개수를 셀 때 사용한다.
  • 예제:
     
List<String> list = Arrays.asList("apple", "banana", "cherry");
long count = list.stream().count();
// count: 3

5. findFirst

  • 설명: 스트림에서 첫 번째 요소를 반환한다. 요소가 없으면 빈 Optional을 반환한다.
  • 용도: 스트림의 첫 번째 요소를 찾을 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
Optional<String> firstElement = list.stream().findFirst();
// firstElement: Optional[apple]

6. findAny

  • 설명: 스트림에서 임의의 요소를 반환한다. 요소가 없으면 빈 Optional을 반환한다.
  • 용도: 병렬 스트림에서 성능 최적화를 위해 임의의 요소를 찾을 때 사용한다.
  • 예제:
     
List<String> list = Arrays.asList("apple", "banana", "cherry");
Optional<String> anyElement = list.stream().findAny();
// anyElement: Optional[apple] (또는 다른 임의의 요소)

7. anyMatch

  • 설명: 스트림의 어떤 요소라도 주어진 조건(predicate)을 만족하면 true를 반환한다.
  • 용도: 스트림에 조건을 만족하는 요소가 하나라도 있는지 확인할 때 사용한니다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean hasApple = list.stream().anyMatch(s -> s.equals("apple"));
// hasApple: true

8. allMatch

  • 설명: 스트림의 모든 요소가 주어진 조건(predicate)을 만족하면 true를 반환한다.
  • 용도: 스트림의 모든 요소가 조건을 만족하는지 확인할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean allHaveA = list.stream().allMatch(s -> s.contains("a"));
// allHaveA: true

9. noneMatch

  • 설명: 스트림의 모든 요소가 주어진 조건(predicate)을 만족하지 않으면 true를 반환한다.
  • 용도: 스트림의 모든 요소가 조건을 만족하지 않는지 확인할 때 사용한다.
  • 예제:
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean noneStartWithZ = list.stream().noneMatch(s -> s.startsWith("z"));
// noneStartWithZ: true

 

Stream 사용 예제 

컬렉션에서 스트림 생성

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Edward");

List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("A"))  // 이름이 "A"로 시작하는 항목 필터링
    .map(String::toUpperCase)              // 이름을 대문자로 변환
    .sorted()                              // 이름을 정렬
    .collect(Collectors.toList());         // 결과를 리스트로 수집

filteredNames.forEach(System.out::println);  // 결과 출력: ALICE

 

배열에서 스트림 생성

int[] numbers = {1, 2, 3, 4, 5};

int sum = Arrays.stream(numbers)
    .filter(n -> n % 2 == 0)  // 짝수 필터링
    .sum();                   // 합계 계산

System.out.println(sum);  // 결과 출력: 6

 

파일에서 스트림 생성

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    long lineCount = lines.count();  // 파일의 줄 수 계산
    System.out.println(lineCount);
} catch (IOException e) {
    e.printStackTrace();
}

 

 

 

 

728x90
반응형