Home [Java 프로그래밍] 12강- 컬렉션과 스트림
Post
Cancel

[Java 프로그래밍] 12강- 컬렉션과 스트림

💡해당 게시글은 방송통신대학교 김희천 교수님의 'Java 프로그래밍' 강의를 개인 공부 목적으로 메모하였습니다.



학습 개요


  • 스트림은 컬렉션이나 배열로부터 만들어지는 원소의 시퀀스를 표현하며, 간결하고 효율적인 다양한 처리 방법을 제공하는 인터페이스임
  • forEach()메소드를 사용하고 매개 변수로 람다 식을 제공하면 컬렉션이나 배열의 내부에서 반복을 처리함
  • 스트림이 제공하는 다양한 기능을 활용하여 컬렉션이나 배열의 원소를 처리하는 방법을 학습함



학습 목표


  • 내부 반복과 외부 반복의 차이점을 설명할 수 있음
  • forEach()메소드를 활용할 수 있음
  • 스트림을 이용하여 컬렉션/배열의 원소에 대한 병렬 처리 방법을 설명할 수 있음
  • 스트림 파이프 라인을 이해하고 중간 처리과 최종 처리의 차이점을 설명할 수 있음



강의록


forEach()메소드

외부 반복

  • 컬렉션이나 배열의 원소를 다룰 때, 원소의 반복 처리를 프로그램에서 명시적으로 제어하는 방식
    • 원소를 프로그램에서 선언된 변수로 복사한 후, 작업을 처리함
    • for, 향상된 for, while, do-while, Iterator등을 이용한 반복 작업이 외부 반복에 해당됨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      List<String> names = Arrays.asList("Kim", "Lee", "Park");
        
      for (String name : names) {
          System.out.println(name);
      }
        
      Iterator<String> iterator = names.listIterator();
        
      while (iterator.hasNext()) {
          System.out.println(iterator.next());
      }
    

내부 반복과 forEach()메소드

  • 원소의 반복 처리를 컬렉션 또는 스트림과 같은 데이터 구조 내부에서 반복을 처리하는 방식
    • 프로그램에서 스트림 API를 이용하여 반복 처리를 위임하고, 처리 코드만 람다식으로 제공함
  • 컬렉션의 forEach()메소드를 이용하고, 매개변수로 람다식을 지정
    • forEach()메소드는 함수형 인터페이스인 Consumer객체를 인자로 받음
    1
    2
    3
    4
    
      List<String> names = Arrays.asList("Kim", "Lee", "Park");
        
      // 내부 반복
      names.forEach(item -> System.out.println("내부 반복: " + item));
    
  • 코드가 간결해 지며, 가독성이 좋아지고, 병렬 처리가 가능하며, 성능 최적화에 유리

스트림

스트림 정의

  • 컬렉션이나 배열과 같은 데이터 소스로부터 만들어지는 원소의 시퀀스를 표현하며, 간결하고 효율적 처리 방법을 제공하는 인터페이스
    • 내부 반복과 함수형 프로그래밍 방식을 지원
    • 멀티 코어 CPU를 활용한 병렬 처리 지원
    • 관련 클래스와 인터페이스는 java.util.stream패키지에 있음
    • 다양한 집합체 연산을 지원함
      • 중간 처리 연산과 최종 처리 연산
      • filter(), map(), sorted(), count(), collect(), anyMatch()

스트림의 특성

  • 데이터 원본으로부터 스트림을 생성하며, 원본 데이터를 변경하지 않음
    • 원본 데이터의 변경이 필요하면 새로운 스트림을 생성해야 함
    • 스트림은 일회용
    • 스트림 연산을 파이프라인 형태로 연결할 수 있음
    • 지연 평가(lazy evaluation)를 통해 연산을 최적화
  • java.util.stream패키지에 포함된 스트림 인터페이스

    image.png

숫자와 스트림

  • IntStream, LongStream, DoubleStream인터페이스
    • 기본형 int, long, double형의 원소로 이루어진 데이터를 다루기 위한 스트림
    • 주요 기능
      • ex) IntStream의 경우
        • static IntStream range(int, int), static IntStream rangeClosed(int, int)
        • static IntStream of(int... values)
        • sum(), average(), min(), max()
        • filter(), map(), reduce()
      1
      2
      3
      4
      
        System.out.println(IntStream.rangeClosed(1, 100).sum());
        System.out.println(IntStream.rangeClosed(1, 100).average().getAsDouble());
        System.out.println(IntStream.rangeClosed(1, 100).min().getAsInt());
        System.out.println(IntStream.rangeClosed(1, 100).max().getAsInt());
      

배열과 스트림

  • Arrays.stream()를 사용하여 배열로부터 스트림을 생성할 수 있음
  • Arrays클래스에서 제공되는 메소드

    1
    
      static DoubleStream stream(double[] array)
    
    1
    
      static IntStream stream(int[] array)
    
    1
    
      static <T> Stream<T> stream(T[])
    
    • 원소가 객체인 배열로부터 스트림 생성
    • Stream은 제네릭 인터페이스
    1
    2
    3
    4
    5
    6
    7
    
      String[] strArray = { "홍길동", "이순신", "김유신" };
      Stream<String> strStream = Arrays.stream(strArray);
      strStream.forEach(item -> System.out.println(item));
        
      int[] intArray = {1, 2, 3};
      IntStream intStream = Arrays.stream(intArray);
      intStream.forEach(item -> System.out.println(item));
    

파일과 스트림

  • Files.lines(Path)을 사용하여 텍스트 파일로부터 행 단위 문자열로 구성 된 스트림을 생성할 수 있음

    1
    2
    3
    4
    
      Path path = Paths.get("c:\\data\\data.txt");
      Stream<String> fileStream = Files.lines(path);
      fileStream.forEach(line -> System.out.println(line));
      fileStream.close();
    

컬렉션과 스트림

  • Collection유형의 객체인 경우
    • Collection인터페이스에서 stream()parallelStream()메소드가 디폴트 메소드로 제공 됨
      • parallelStream()은 병렬 처리가 가능한 스트림을 리턴함
    • HashSet, ArrayList, LinkedList객체 등으로부터 스트림을 생성할 때 사용
  • HashMap객체인 경우
    • 먼저 entrySet()을 사용하여 Set 유형의 객체를 얻은 후, stream()또는 parallelStream()메소드를 사용하여 스트림을 생성함

HashSet과 스트림 사용 예

  • HashSet객체와 스트림

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
      import java.util.*;
      import java.util.stream.*;
        
      public class Main {
          public static void main(String[] args) {
              Set<Integer> set = new HashSet<>();
              for (int i = 0; i < 10; i++) {
                  set.add(i);
              }
        
              // 일반 스트림 사용
              System.out.println("일반 스트림 사용");
              Stream<Integer> n_stream = set.stream();
              n_stream.forEach(item -> System.out.println(item));
        
              // 병렬 스트림 사용
              System.out.println("병렬처리 스트림 사용");
              Stream<Integer> p_stream = set.parallelStream();
              p_stream.forEach(item -> System.out.println(item + "(" +
                  Thread.currentThread().getName() + ")"));
          }
      }
        
      // 일반 스트림 사용
      // 0
      // 1
      // 2
      // 3
      // 4
      // 5
      // 6
      // 7
      // 8
      // 9
      // 병렬처리 스트림 사용
      // 6(ForkJoinPool.commonPool-worker-2)
      // 7(ForkJoinPool.commonPool-worker-2)
      // 4(ForkJoinPool.commonPool-worker-1)
      // 5(ForkJoinPool.commonPool-worker-1)
      // 8(main)
      // 9(main)
      // 2(ForkJoinPool.commonPool-worker-3)
      // 0(ForkJoinPool.commonPool-worker-4)
      // 1(ForkJoinPool.commonPool-worker-4)
      // 3(ForkJoinPool.commonPool-worker-3)
    

ArrayList와 스트림 사용 예

  • 객체를 원소로 갖는 ArrayList와 스트림

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
      class People {
          String name;
          int age;
        
          People(String name, int age) {
              this.name = name;
              this.age = age;
          }
        
          public String toString() {
              return ("name : " + this.name + ", age : " + this.age);
          }
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
      import java.util.*;
      import java.util.stream.*;
        
      public class Main {
          public static void main(String[] args) {
              List<People> list = new ArrayList<People>();
              list.add(new People("홍길동", 30));
              list.add(new People("이순신", 40));
              list.add(new People("김유신", 50));
              list.add(new People("유관순", 20));
        
              // 일반 스트림 사용
              System.out.println("일반 스트림 사용");
              Stream<People> n_stream = list.stream();
              n_stream.forEach(item -> System.out.println(item));
        
              // 병렬 스트림 사용
              System.out.println("병렬처리 스트림 사용");
              Stream<People> p_stream = list.parallelStream();
              p_stream.forEach(item -> System.out.println(item + " : " + Thread.currentThread().getName()));
          }
      }
      // 일반 스트림 사용
      // name : 홍길동, age : 30
      // name : 이순신, age : 40
      // name : 김유신, age : 50
      // name : 유관순, age : 20
      // 병렬처리 스트림 사용
      // name : 김유신, age : 50 : main
      // name : 이순신, age : 40 : ForkJoinPool.commonPool-worker-1
      // name : 홍길동, age : 30 : main
      // name : 유관순, age : 20 : ForkJoinPool.commonPool-worker-2
    

스트림 파이프라인

스트림 파이프라인과 중간 연산

  • 컬렉션, 배열, 또는 파일로부터 생성 된 스트림에 어떤 처리를 수행하고 새로운 스트림이 만들어지며, 계속해서 이러한 처리 과정을 반복하는 것
    • 메소드 체이닝을 사용하여 파이프라인을 구축할 수 있음
  • 중간 연산(또는 중간 처리)
    • 원본 스트림에서 데이터를 반환하거나 필터링 등으로 새로운 스트림을 생성하는 연산
    • 중간 연산에서 생성 된 스트림은 다음 연산으로 연결 되어 파이프라인을 형성 함
    • filter(), map(), sorted()등의 연산

    image.png

스트림 파이프라인과 종료 연산

  • 종료 연산(또는 최종 연산)
    • 중간 연산을 거친 스트림에 대해 최종적인 결과를 만들거나 동작을 수행하는 부분
    • 스트림 파이프라인에서 마지막에 한번만 수행 됨
    • forEach(), collect(), count(), anyMatch, reduce()
    1
    2
    3
    4
    5
    6
    7
    8
    
      List<String> words = Arrays.asList("apple", "banana", "cherry", "Avocado");
        
      long count = words.stream() // 원본 스트림
          .map(String::toUpperCase) // 중간 연산
          .filter(word -> word.startsWith("A")) // 중간연산
          .count(); // 최종연산
        
      System.out.println(count); // 2가 출력됨
    

스트림 파이프라인과 메소드 체이닝

  • 메소드 체이닝의 활용 - 메소드의 연속적 호출

    1
    2
    3
    4
    5
    6
    
      List<Book> books = new ArrayList<Book>();
      // ... ...
      Stream<Book> stream = books.stream();
      IntStream price = stream.mapToInt(book -> book.getPrice());
      double avg = price.average().getAsDouble();
      System.out.println(avg);
    
    1
    2
    3
    4
    5
    6
    
      List<Book> books = new ArrayList<Book>();
      // ... ...
      double avg = books.stream()
          .mapToInt(book -> book.getPrice())
          .average().getAsDouble();
      System.out.println(avg);
    

    image.png

중간 연산

필터링

  • 컬렉션 원소들 중에서 중복을 제거하거나 특정 조건을 만족하는 원소만 추출하여 새로운 스트림을 생성하는 작업
    • Stream<T>, IntStream, LongStream, DoubleStream에서 필터링 메소드는 중간 연산이므로 같은 스트림을 리턴 함
    • 중복 제거에는 distinct()메소드를 사용
      • 원소가 객체인 경우 hashCode()의 리턴 값이 같고, equals()로 비교할 때 true인 경우, 중복으로 판단함
    • 조건을 통한 걸러 내기에는 filter()메소드를 사용
      • filter()메소드의 매개 변수는 함수형 인터페이스이며, true 또는 false를 리턴하는 람다식을 매개 변수로 전달

필터링 예

  • distinct()메소드로 중복을 제거하는 필터링 스트림 예

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
      import java.util.*;
        
      class Book {
          String title;
          String author;
          int price;
        
          Book(String title, String author, int price) {
              this.title = title;
              this.author = author;
              this.price = price;
          }
        
          @Override
          public String toString() {
              return "title:" + title + ", author:" + author + ", price:" + price;
          }
        
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Book book = (Book) o;
              return price == book.price &&
                  Objects.equals(title, book.title) &&
                  Objects.equals(author, book.author);
          }
        
          @Override
          public int hashCode() {
              return Objects.hash(title, author, price);
          }
      }
        
      public class Main {
          public static void main(String[] args) {
              List<Book> books = new ArrayList<Book>();
              books.add(new Book("JAVA 프로그래밍", "홍길동", 10000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("빅데이터 연구", "유관순", 12000));
              books.add(new Book("커뮤니케이션 이론", "강감찬", 15000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("알고리즘", "권율", 17000));
        
              books.stream()
                  .distinct()
                  .forEach(book -> System.out.println(book));
          }
      }
      // title:JAVA 프로그래밍, author:홍길동, price:10000
      // title:PHP 프로그래밍, author:이순신, price:20000
      // title:빅데이터 연구, author:유관순, price:12000
      // title:커뮤니케이션 이론, author:강감찬, price:15000
      // title:알고리즘, author:권율, price:17000
    
  • filter()메소드를 사용하는 필터링 스트림 예

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      import java.util.*;
        
      public class Main {
          public static void main(String[] args) {
              String[] strArray = {"ABC", "BCD", "AFE", "CDE", "ABZ", "ACCZ"};
              List <String> strList = Arrays.asList(strArray);
        
              strList.stream()
                  .filter(item -> item.startsWith("A"))
                  .filter(item -> item.endsWith("Z"))
                  .filter(item -> item.length() > 3)
                  .forEach(item -> System.out.println(item));
        
              strList.stream()
                  .filter(item -> item.startsWith("A") && item.endsWith("Z") && item.length() > 3)
                  .forEach(item -> System.out.println(item));
          }
      }
        
      // ACCZ
      // ACCZ
    

매핑

  • 원소들을 다른 원소로 변환하여 새로운 스트림을 생성하는 작업
    • Stream<T>에서 map()은 매개 변수로 주어지는 함수(람다식)를 적용하여 Stream<T>로, mapToInt()은 함수를 적용하여 IntStream으로 변환 시킴
    • flatMap()flatMapToInt()등은 스트림의 각 요소에 매핑 함수(람다식)를 적용하여 스트림으로 변환한 후, 여러 스트림을 다시 하나의 스트림으로 합침
    • IntStream에서 asDoubleStream()asLongStream()은 기본형을 다른 기본형으로 변환 시키는 것으로 DoubleStream또는 LongStream객체를 리턴, boxed()는 기본형 int 값을 포장형 Integer객체로 변환시키고 Stream<Integer>객체를 리턴

매핑 예

  • 스트림 매핑 예

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
      import java.util.*;
      import java.util.stream.*;
        
      public class Main {
          public static void main(String[] args) {
              //String 배열을 IntStream으로 변환
              String[] strNums = {"10", "20", "30"};
              Arrays.stream(strNums)
                  .mapToInt(item -> Integer.parseInt(item))
                  .forEach(item -> System.out.println(item));
        
              //int 배열을 Stream<String>으로 변환
              int[] numbers = {10, 20, 30};
              Arrays.stream(numbers)
                  .mapToObj(item -> String.valueOf(item))
                  .forEach(item -> System.out.println(item + "(" + item.length() +")"));
          }
      }
        
      // 10
      // 20
      // 30
      // 10(2)
      // 20(2)
      // 30(2)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      import java.util.*;
      import java.util.stream.*;
        
      public class Main {
          public static void main(String[] args) {
              Arrays.asList("Hello world, this is java", "Welcome to java world")
                  .stream()
                  .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
                  .forEach(word -> System.out.println(word));
          }
      }
        
      // Hello
      // world,
      // this
      // is
      // java
      // Welcome
      // to
      // java
      // world
    

정렬

  • 스트림의 원소들을 오름차순 또는 내림차순으로 정렬하여 새로운 스트림을 생성하는 작업
    • Stream<T>에서 sorted()메소드 사용
      • 정렬 된 새로운 스트림을 리턴
      • T 유형의 객체(원소)는 Comparable이어야 하며, 크기 비교에 compareTo()메소드가 사용 됨
      • 기본형에 대응 되는 포장 클래스는 모두 Comparable

정렬 예

  • compareTo()가 구현 된 Book 클래스와 정렬 된 스트림 생성 예

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
      class Book implements Comparable<Book> {
          String title;
          String author;
          int price;
        
          Book(String title, String author, int price) {
              this.title = title;
              this.author = author;
              this.price = price;
          }
        
          public String toString() {
              return ("title:" + this.title + ", author:" + this.author + ", price:" + this.price);
          }
        
          @Override
          public int compareTo(Book book) {
              return Integer.compare(this.price, book.price);
          }
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
      import java.util.*;
        
      public class Main {
          public static void main(String[] args) {
              List<Book> books = new ArrayList<Book>();
              books.add(new Book("JAVA 프로그래밍", "홍길동", 10000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("마이크로프로세서 입문", "김유신", 14000));
              books.add(new Book("데이터베이스 입문", "신사임당", 21000));
              books.add(new Book("빅데이터 연구", "유관순", 12000));
              books.add(new Book("커뮤니케이션 이론", "강감찬", 15000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("알고리즘", "권율", 17000));
        
              books.stream().sorted().forEach(book->
                  System.out.println(book));
          }
      }
        
      // title:JAVA 프로그래밍, author:홍길동, price:10000
      // title:빅데이터 연구, author:유관순, price:12000
      // title:마이크로프로세서 입문, author:김유신, price:14000
      // title:커뮤니케이션 이론, author:강감찬, price:15000
      // title:알고리즘, author:권율, price:17000
      // title:PHP 프로그래밍, author:이순신, price:20000
      // title:PHP 프로그래밍, author:이순신, price:20000
      // title:데이터베이스 입문, author:신사임당, price:21000
    

루핑

  • 스트림의 원소들을 하나씩 순회하면서 반복적으로 처리하고 새로운 스트림을 생성하는 작업
    • peek()메소드는 각 요소를 순회하면서 주어진 동작(람다식)을 수행하는 중간 연산으로 디버깅이나 로깅에 자주 사용 됨
    • 새로운 스트림을 반환하며, 최종 연산과 함께 사용되어야 함
      • 참고로 forEach()메소드는 각 요소를 반복하면서 주어진 동작(람다식)을 수행하는 종료 연산
      • anyMatch()메소드는 종료 연산으로 주어진 조건(람다식)을 만족하는지 원소를 순회하면서 조사함
        • 주어진 조건을 만족하는 요소를 찾으면 즉시 검색을 중단
      • 스트림 파이프라인에서 peek()anyMatch()를 조합하면 처리되는 원소를, 조건식에서 처음 true가 되는 원소까지로 제한하게 됨
  • 주로 디버깅 용도로 사용됨
  • 종료 연산이 있어야 작동함

루핑 예

  • peek()메소드 사용 예

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
      import java.util.*;
      import java.util.stream.*;
        
      class Book { 
          String title;
          String author;
          int price;
        
          Book(String title, String author, int price) {
              this.title = title;
              this.author = author;
              this.price = price;
          }
        
          public String toString() {
              return ("title:" + this.title + ", author:" +
                  this.author + ", price:" + this.price);
          }
      }
        
      public class Main {
          public static void main(String[] args) {
              List <Book> books = new ArrayList <Book> ();
              books.add(new Book("JAVA 프로그래밍", "홍길동", 10000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("마이크로프로세서 입문", "김유신", 14000));
              books.add(new Book("데이터베이스 입문", "신사임당", 21000));
              books.add(new Book("빅데이터 연구", "유관순", 12000));
              books.add(new Book("커뮤니케이션 이론", "강감찬", 15000));
              books.add(new Book("PHP 프로그래밍", "이순신", 20000));
              books.add(new Book("알고리즘", "권율", 17000));
        
              books.stream()
                  .peek(book -> System.out.println(book))
                  .anyMatch(m -> m.price > 20000); // 종료 연산
          }
      }
        
      // anyMatch가 true를 반환하기 전까지 peek 실행
        
      // title:JAVA 프로그래밍, author:홍길동, price:10000
      // title:PHP 프로그래밍, author:이순신, price:20000
      // title:마이크로프로세서 입문, author:김유신, price:14000
      // title:데이터베이스 입문, author:신사임당, price:21000`
    

종료 연산

종료 연산이란

  • 중간 처리를 거친 스트림에 대해 집계나 결과 출력 등의 최종 처리를 수행하는 최종 연산
    • 집계(합계, 평균, 최대, 최소 등), 매칭, 수집 등의 작업을 수행함
  • 스트림 파이프라인의 마지막 단계에서 사용되며, 스트림을 다시 사용할 수 없게 함
    • 종료 연산이 수행되기 전까지는 스트림 파이프라인이 실제로 실행 되지 않음(lazy evaluation)
    • 지연 평가를 통해 스트림 파이프 라인을 효율적 구성하며 필요한 결과를 얻음

집계

  • 원소들의 개수, 합계, 평균, 최대 값, 최소 값 등을 구하는 최종 처리
  • count(), sum(), average(), min(), max(), reduce()
    • count()
      • 스트림의 원소 개수 반환
    • sum()
      • 숫자 스트림의 합계 반환
    • average()
      • 숫자 스트림의 평균 반환 (OptionalDouble반환)
    • min()
      • 스트림의 최소 값 반환 (OptionalInt, OptionalLong, OptionalDouble반환)
    • max()
      • 스트림의 최대 값 반환 (OptionalInt, OptionalLong, OptionalDouble반환)
    • reduce()
      • 사용자 정의 집계 연산 수행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 1부터 10까지의 정수 중에서 짝수만 필터링하여 출력
long count = Arrays.stream(intArray).filter(n -> n % 2 == 0).count();
System.out.println("2의 배수의 개수 : " + count);

// 1부터 10까지의 정수 중에서 짝수만 필터링하여 합계 계산
long sum = Arrays.stream(intArray).filter(n -> n % 2 == 0).sum();
System.out.println("2의 배수의 합 : " + sum);

// 1부터 10까지의 정수 중에서 짝수만 필터링하여 최소 값 계산
OptionalInt min = Arrays.stream(intArray).min();
System.out.println("최소값 : " + min.getAsInt());

// 1부터 10까지의 정수 중에서 짝수만 필터링하여 평균 계산
OptionalDouble avg = Arrays.stream(intArray).average();
System.out.println("평균 : " + avg.getAsDouble());

// 2의 배수의 개수 : 5
// 2의 배수의 합 : 30
// 최소값 : 1
// 평균 : 5.5

매칭

  • 스트림의 원소가 특정 조건을 만족하는지 검사하는 연산
  • anyMatch()
    • 스트림의 최소 하나의 요소가 조건을 만족할 때 true 반환
  • allMatch()
    • 스트림의 모든 원소가 조건을 만족할 때 true 반환
  • noneMatch()
    • 스트림의 모든 원소가 조건을 만족하지 않으면 true 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
boolean result = false;

result = Arrays.stream(intArray).anyMatch(n -> n % 2 == 0);
System.out.println("적어도 하나는 2의 배수인가? " + result);

result = Arrays.stream(intArray).allMatch(n -> n % 2 == 0);
System.out.println("모두 2의 배수인가? " + result);

result = Arrays.stream(intArray).filter(n -> n % 2 != 0)
        .noneMatch(n -> n % 2 == 0);
System.out.println("모두 2의 배수가 아닌가? " + result);

// 적어도 하나는 2의 배수인가? true
// 모두 2의 배수인가? false
// 모두 2의 배수가 아닌가? true

수집

  • 스트림의 원소들을 필터링 또는 매핑한 후 최종적으로 새로운 컬렉션(List, Set, Map)을 생성하는 종료 연산
  • Collectors클래스는 스트림의 요소들을 수집하는 데 사용 되는 다양한 static 유틸리티 메소드를 제공
    • 이것의 결과가 collect()의 인자로 사용 됨
  • collect()
    • Collector를 인자로 받아 다양한 수집 작업 수행
    • Collectors.toList()
      • List로 수집
    • Collectors.toSet()
      • Set으로 수집
    • Collectors.toMap()
      • Map으로 수집
    • Collectors.groupingBy()
      • 특정 기준으로 그룹화하여 Map으로 수집
    • Collectors.joining()
      • 문자열 스트림의 원소를 연결
1
2
3
4
5
6
7
8
9
10
11
12
List<Member> male_members = members.stream().filter(member->
member.getGender().equals("남")).collect(Collectors.toList());
male_members.stream().forEach(member -> System.out.println(member));

Set<Member> female_members2 = members.stream().filter(member ->
member.getGender().equals("여")).collect(Collectors.toSet());
female_members2.stream().forEach(member -> System.out.println(member));

Map<String, Integer> ages =
female_members2.stream().collect(Collectors.toMap(member ->
member.getName(), member -> member.getAge()));
System.out.println(ages);



학습 정리


  • 내부 반복은 컬렉션이나 배열의 내부에서 각 원소의 반복 처리를 수행하는 것으로, 처리용 코드만 람다 식으로 전달하며 반복 작업을 위한 코드를 작성하지 않음
  • forEach()메소드는 람다 식과 함께 사용하여 컬렉션과 배열의 원소들을 외부로 꺼내오지 않고 내부에서 탐색하게 함
  • 필터링은 스트림의 원소 중에서 중복을 제거하거나 특정 조건을 만족하는 원소만 걸러 내고 새로운 스트림을 리턴하는 중간 연산임
  • 매핑은 스트림의 원소를 다른 원소로 변환한 후 새로운 스트림으로 리턴하는 중간 연산임
  • 매칭은 스트림의 원소 중 특정 조건을 만족하는지 확인하는 최종 연산임
  • 수집은 스트림의 원소 중 필터링 또는 매핑 작업 후 결과 원소들을 취합하여 새로운 List, Set, 또는 Map객체를 생성하는 최종 연산임



연습 문제


  1. 주어진 배열을 스트림으로 만드려고 한다. 밑줄 부분에 들어갈 적당한 내용은?

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

    a. Arrays.stream(numbers)

  2. 주어진 문자열 배열에서 길이가 6 이상인 단어를 대문자로 변환한 후 정렬하여 출력하려고 한다. 밑줄 부분에 들어갈 메소드는 무엇인가?

    1
    2
    3
    4
    5
    6
    7
    8
    
     List<String> words = Arrays.asList("banana", "orange", "grape", "strawberry", "kiwi");
     List<String> result = words.stream()
         // .______(word -> word.length() >= 5) // 빈칸
         .map(str -> str.toUpperCase())
         .sorted()
         .collect(Collectors.toList());
        														
     System.out.println(result);
    

    a. filter

  3. 중간 연산과 종료 연산의 차이를 설명하시오.

    a. 중간 연산은 스트림을 변환하거나 필터링 하는 등의 작업을 수행하며, 다음 단계 처리를 위해 새로운 스트림을 리턴하는데, 체인 형태로 연속해 여러 번 호출될 수 있다. 종료 연산은 스트림의 원소를 이용해 최종 결과를 만들어 리턴한다.

[유비쿼터스 컴퓨팅 개론] 11강 - 인간과 컴퓨터/인간과 로봇 상호 작용 기술

[데이터베이스 시스템] 12강 - 해싱과 특수 인덱스