본문 바로가기
JAVA

[JAVA] - Stream API 이해 4

by nam_ji 2024. 5. 29.

Stream API 이해

1. 데이터 선별

  • 조건에 따라 데이터를 선별하는 중간 연산입니다.
  • 데이터 선별 예제
    더보기
    package lambdaExample;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 선별------------------");
        DataSelect dataSelect = new DataSelect();
        dataSelect.dataSelect();
      }
    }
    
    class DataSelect {
      public void dataSelect () {
        ListClass listClass = new ListClass();
        listClass.cities.add(new ExCity("Seoul", 0, 0, "02"));
    
        System.out.println("배열 추가 시");
        for (ExCity city : listClass.cities) {
          System.out.println(
              city.getName() + " " +
              city.getArea() + " " +
              city.getPopulation() + " " +
              city.getAreaCode());
        }
    
        List<ExCity> streamNameList = listClass.cities.stream()
            .filter(city -> city.getArea() > 600) // 데이터 필터링
            .distinct()                           // 중복 제거
            .collect(Collectors.toList());
    
        System.out.println("\n중복 제거 시");
        for (ExCity city : streamNameList) {
          System.out.println(
              city.getName() + " " +
              city.getArea() + " " +
              city.getPopulation() + " " +
              city.getAreaCode());
        }
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 선별------------------
    배열 추가 시
    Seoul 605.2 9720846 02
    Incheon 1063.3 2947217 032
    Ulsan 1062.0 1142190 052
    Daegu 883.5 2427954 053
    Gwangju 501.1 1455048 062
    Busan 770.1 3404423 051
    Seoul 0.0 0 02
    
    중복 제거 시
    Seoul 605.2 9720846 02
    Incheon 1063.3 2947217 032
    Ulsan 1062.0 1142190 052
    Daegu 883.5 2427954 053
    Busan 770.1 3404423 051
    */
  • Stream::filter는 Predicate를 인자로 받습니다.
  • 중복 요소를 제거하고 싶다면 Stream::distinct를 사용합니다.

2. 데이터 개수 조절

  • 스트림 데이터를 자르거나 특정 요소만 성택합니다.
  • 개수 제한 Stream::limit으로 개수 조절이 가능합니다.
  • 건너 뛰기 Stream::skip으로 n개를 건너뛸 수 있습니다.
  • 데이터 개수 조절 예제
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 개수 조절------------------");
        DataCountControl dataCountControl = new DataCountControl();
        dataCountControl.dataCountControl();
      }
    }
    
    class DataCountControl {
      public void dataCountControl() {
        ListClass listClass = new ListClass();
    
        // limit을 이용하여 수집 개수 조절
        List<String> streamLimit = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .sorted(Comparator.comparing(ExCity::getArea))
            .map(ExCity::getName)
            .limit(2)                               // 최소 2개의 원소만 반환합니다.
            .collect(Collectors.toList());
    
        System.out.println("Stream Limit");
        System.out.println(streamLimit);
    
        // skip을 이용하여 데이터 건너뛰기
        List<String> streamSkip = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .sorted(Comparator.comparing(ExCity::getArea))
            .map(ExCity::getName)
            .skip(1)                                      // 첫 번째 원소는 뛰어넘습니다.
            .collect(Collectors.toList());
    
        System.out.println("Stream Skip");
        System.out.println(streamSkip);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 개수 조절------------------
    Stream Limit
    [Daegu, Ulsan]
    Stream Skip
    [Ulsan, Incheon]
    */
  • 특정 조건에 부합하는 데이터만 선택합니다.
  • 전제조건으로 자바 9버전 이상부터 지원됩니다.
  • Stream::takeWhile은 스트림을 순회하면서 false가 되는 순간 남은 데이터를 버립니다.
  • Stream::dropWhile은 스트림을 순회하면서 true가 되는 순간 남은 데이터를 버립니다.
  • Stream::filter와 차이점은 필터는 조건에 맞는 데이터만 선택하지만, takeWhile과 dropWhile은 데이터를 버립니다.
  • 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 선별2------------------");
        DataSelect2 dataSelect2 = new DataSelect2();
        dataSelect2.dataSelect2();
    
        System.out.println("\n------------------Stream 데이터 선별3------------------");
        DataSelect3 dataSelect3 = new DataSelect3();
        dataSelect3.dataSelect3();
      }
    }
    
    class DataSelect3 {
      public void dataSelect3 () {
        ListClass listClass = new ListClass();
    
        // dropWhile은 true가 되면 멈추고 나머지 데이터는 버립니다.
        List<String> streamSelectAsc = listClass.cities.stream()
            .sorted(Comparator.comparing(ExCity::getArea))
            .dropWhile(exCity -> exCity.getArea() > 700)
            .map(ExCity::getName)
            .collect(Collectors.toList());
    
        System.out.println("dropWhile 오름차순 정렬");
        System.out.println(streamSelectAsc);
    
        List<String> streamSelectDesc = listClass.cities.stream()
            .sorted(Comparator.comparing(ExCity::getArea).reversed())
            .dropWhile(exCity -> exCity.getArea() > 700)
            .map(ExCity::getName)
            .collect(Collectors.toList());
    
        System.out.println("dropWhile 내림차순 정렬");
        System.out.println(streamSelectDesc);
      }
    }
    
    class DataSelect2 {
      public void dataSelect2 () {
        ListClass listClass = new ListClass();
    
        // takeWhile은 false가 되면 멈추고 나머지 데이터는 버립니다.
        List<String> streamSelectAsc = listClass.cities.stream()
            .sorted(Comparator.comparing(ExCity::getArea))
            .takeWhile(exCity -> exCity.getArea() > 700)
            .map(ExCity::getName)
            .collect(Collectors.toList());
    
        System.out.println("takeWhile 오름차순 정렬");
        System.out.println(streamSelectAsc);
    
        // area를 역순 정렬하여 비교해 보겠습니다.
        List<String> streamSelectDesc = listClass.cities.stream()
            .sorted(Comparator.comparing(ExCity::getArea).reversed())
            .takeWhile(exCity -> exCity.getArea() > 700)
            .map(ExCity::getName)
            .collect(Collectors.toList());
    
        System.out.println("takeWhile 내림차순 정렬");
        System.out.println(streamSelectDesc);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 선별2------------------
    takeWhile 오름차순 정렬
    []
    takeWhile 내림차순 정렬
    [Incheon, Ulsan, Daegu, Busan]
    
    ------------------Stream 데이터 선별3------------------
    dropWhile 오름차순 정렬
    [Gwangju, Seoul, Busan, Daegu, Ulsan, Incheon]
    dropWhile 내림차순 정렬
    [Seoul, Gwangju]
    */

3. 데이터 변환

  • Stream::map은 스트림 원소를 변환합니다.
  • 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 변환------------------");
        DataConversion dataConversion = new DataConversion();
        dataConversion.dataConversion();
      }
    }
    
    class DataConversion {
      public void dataConversion () {
        ListClass listClass = new ListClass();
    
        List<String> streamMap = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .sorted(Comparator.comparing(ExCity::getArea))
            .map(exCity -> exCity.getName())
    //        .map(ExCity::getName) 위와 똑같은 역할입니다.
            .collect(Collectors.toList());
    
        System.out.println("Stream Map");
        System.out.println(streamMap);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 변환------------------
    Stream Map
    [Daegu, Ulsan, Incheon]
    */

1. 단일 스트림으로 변환

  • Stream::flatMap은 배열의 데이터를 문자열 하나로 만들거나 문자열을 문자 단위로 분해하는 과정이 필요할 때 사용합니다.
  • Stream::flatMap 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 변환2------------------");
        DataConversion2 dataConversion2 = new DataConversion2();
        dataConversion2.dataConversion2();
      }
    }
    
    class DataConversion2 {
      public void dataConversion2 () {
        ListClass listClass = new ListClass();
    
        List<String> streamMap2 = listClass.cities.stream()
            .map(ExCity::getAreaCode)
            .collect(Collectors.toList());
    
        System.out.println("Stream Map2");
        System.out.println(streamMap2);
    
        List<String> streamSplit = streamMap2.stream()
            .map(areaCode -> areaCode.split(""))
            .flatMap(Arrays::stream)
            .distinct()
            .collect(Collectors.toList());
    
        System.out.println("Stream Split");
        System.out.println(streamSplit);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 변환2------------------
    Stream Map2
    [02, 032, 052, 053, 062, 051]
    Stream Split
    [0, 2, 3, 5, 6, 1]
    */

4. 데이터 일치 여부

  • boolean을 반환하는 연산입니다.
  • Short Circuit Evaluation 기법이 적용되어 값을 즉시 반환할 수 있는 상태가 되면 남은 원소를 확인하지 않습니다.
  • Short Circuit 예시
    if (true || false) { ... }
    if (false && true) { ... }
  • Stream::anyMatch는 검색 조건에 맞는 원소가 1개 이상인 경우 true를 반환합니다.
  • Stream::allMatch는 모든 검색 조건이 일치해야 true를 반환합니다.
  • Stream::noneMatch는 모든 원소가 검색 조건에 일치하지 않아야 true를 반환합니다.
  • 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 일치 여부------------------");
        DataMatch dataMatch = new DataMatch();
        dataMatch.dataMatch();
      }
    }
    
    class DataMatch {
      public void dataMatch () {
        ListClass listClass = new ListClass();
    
        boolean streamAnyMatch = listClass.cities.stream()
            .anyMatch(exCity -> exCity.getArea() > 1000);
    
        boolean streamAnyMatch2 = listClass.cities.stream()
            .anyMatch(exCity -> exCity.getArea() > 2000);
    
        System.out.println("Stream Any Math 1000");
        System.out.println(streamAnyMatch);
        System.out.println("Stream Any Math 2000");
        System.out.println(streamAnyMatch2);
    
        boolean streamAllMatch = listClass.cities.stream()
            .allMatch(exCity -> exCity.getArea() > 100);
    
        boolean streamAllMatch2 = listClass.cities.stream()
            .allMatch(exCity -> exCity.getArea() > 1000);
    
        System.out.println("Stream All Match 100");
        System.out.println(streamAllMatch);
        System.out.println("Stream All Match 1000");
        System.out.println(streamAllMatch2);
    
        boolean streamNoneMatch = listClass.cities.stream()
            .noneMatch(exCity -> exCity.getArea() > 1000);
        boolean streamNoneMatch2 = listClass.cities.stream()
            .noneMatch(exCity -> exCity.getArea() > 2000);
    
        System.out.println("Stream None Match 1000");
        System.out.println(streamNoneMatch);
        System.out.println("Stream None Match 2000");
        System.out.println(streamNoneMatch2);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 일치 여부------------------
    Stream Any Math 1000
    true
    Stream Any Math 2000
    false
    Stream All Match 100
    true
    Stream All Match 1000
    false
    Stream None Match 1000
    false
    Stream None Match 2000
    true
    */

5. 데이터 검색

  • 검색 메서드를 사용하면 스트림 파이트라인에서 적절한 데이터를 찾습니다.
  • 또한 데이터를 찾는 순간 검색이 종료되는 Short Circuit 기법이 적용됩니다.
  • 검색 조건에 부합하는 데이터가 없을 수도 있습니다.
  • 따라서 반환 시엔 Optional을 사용합니다.
  • Stream::findAny는 검색 조건에 부합하는 임의의 데이터를 반환합니다.
  • Stream::findFirst는 검색 조건에 부합하는 첫 번째 데이터를 반환합니다.
  • 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 데이터 검색------------------");
        DataSearch dataSearch = new DataSearch();
        dataSearch.dataSearch();
      }
    }
    
    class DataSearch {
      public void dataSearch () {
        ListClass listClass = new ListClass();
    
        Optional<ExCity> findAny = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 500)
            .findAny();
    
        System.out.println("Stream Find Any");
        System.out.println(findAny.get().getName());
    
        Optional<ExCity> findFirst = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 500)
            .findFirst();
    
        System.out.println("Stream Find First");
        System.out.println(findFirst.get().getName());
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 데이터 검색------------------
    Stream Find Any
    Seoul
    Stream Find First
    Seoul
    */

6. 데이터 연산

  • 랜덤 숫자가 담긴 배열의 총 합을 스트림을 활용해 보겠습니다.
  • Stream::reduce는 값을 연쇄적으로 계산할 때 사용합니다.
  • reduce는 (int), IntBinaryOperator를 매개변수로 받습니다.
  • 첫 번째 int는 초기값으로, 생략이 가능합니다.
  • IntBinaryOperator는 두 개의 int형 매개변수를 받아 int를 반환하는 함수형 인터페이스입니다.

1. for loop 활용과 Stream 활용

더보기
package lambdaExample;

import java.nio.file.spi.FileSystemProvider;
import java.util.*;
import java.util.stream.Collectors;
import javax.xml.transform.Source;

public class LambdaBasic2 {
  public static void main(String[] args) {
    System.out.println("\n------------------Stream 데이터 연산------------------");
    DataOperation dataOperation = new DataOperation();
    dataOperation.dataOperation();
  }
}

class DataOperation {
  public void dataOperation () {
    Random random = new Random();

    int[] arr = new int[100];
    for (int i = 0; i < 100; i++) {
      arr[i] = random.nextInt(100);
    }

    int sum = 0;
    for (int num : arr) {
      sum += num;
    }

    System.out.println("일반 연산");
    System.out.println(sum);

    int streamSum = Arrays.stream(arr)
        .reduce(0, Integer::sum);

    System.out.println("스트림 연산");
    System.out.println(streamSum);
  }
}

class ExCity {
  private String name;
  private double area;     // 면적
  private int population;  // 인구
  private String areaCode; // 지역 번호

  // 필드 전체를 파라미터로 받는 생성자
  public ExCity (String name, double area, int population, String areaCode) {
    this.name = name;
    this.area = area;
    this.population = population;
    this.areaCode = areaCode;
  }

  // getter
  public String getName () {
    return name;
  }
  public double getArea () {
    return area;
  }
  public int getPopulation () {
    return population;
  }
  public String getAreaCode () {
    return areaCode;
  }
}

class ListClass {
  List<ExCity> cities = new ArrayList<>(
      Arrays.asList(
          new ExCity("Seoul", 605.2, 9720846, "02"),
          new ExCity("Incheon", 1063.3, 2947217, "032"),
          new ExCity("Ulsan", 1062, 1142190, "052"),
          new ExCity("Daegu", 883.5, 2427954, "053"),
          new ExCity("Gwangju", 501.1, 1455048, "062"),
          new ExCity("Busan", 770.1, 3404423, "051"))
  );
}

/*
출력
------------------Stream 데이터 연산------------------
일반 연산
4777
스트림 연산
4777
*/




7. 최종 연산

  • 최종 연산은 자료형, 컬렉션, void를 반환합니다.
  • 사용 예시
    더보기
    package lambdaExample;
    
    import java.nio.file.spi.FileSystemProvider;
    import java.util.*;
    import java.util.stream.Collectors;
    import javax.xml.transform.Source;
    
    public class LambdaBasic2 {
      public static void main(String[] args) {
        System.out.println("\n------------------Stream 최종 연산------------------");
        FinalStream finalStream = new FinalStream();
        finalStream.finalStream();
      }
    }
    
    class FinalStream {
      public void finalStream() {
        ListClass listClass = new ListClass();
    
        List<String> streamList = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .sorted(Comparator.comparing(ExCity::getArea))
            .map(ExCity::getName)
            .collect(Collectors.toList());
    
        System.out.println("\nStream List");
        System.out.println(streamList);
    
        Map<String, List<ExCity>> streamMap = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .collect(Collectors.groupingBy(ExCity::getName));
    
        System.out.println("\nStream Map");
        streamMap.forEach((city, exCities) -> {
          System.out.println("City : " + city);
          exCities.forEach(exCity -> System.out.println(
              exCity.getName() + " " +
              exCity.getArea() + " " +
              exCity.getAreaCode() + " " +
              exCity.getPopulation()
              ));
        });
    
        Set<ExCity> streamSet = listClass.cities.stream()
            .filter(exCity -> exCity.getArea() > 800)
            .collect(Collectors.toSet());
    
        System.out.println("\nStream Set");
        for (ExCity exCity : streamSet) {
          System.out.println(
              exCity.getName() + " " +
              exCity.getArea() + " " +
              exCity.getAreaCode() + " " +
              exCity.getPopulation());
        }
    
        System.out.println("\nStream Count");
        System.out.println(listClass.cities.stream().count());
    
        System.out.println("\nStream forEach");
        listClass.cities.stream().map(ExCity::getName).forEach(System.out::println);
      }
    }
    
    class ExCity {
      private String name;
      private double area;     // 면적
      private int population;  // 인구
      private String areaCode; // 지역 번호
    
      // 필드 전체를 파라미터로 받는 생성자
      public ExCity (String name, double area, int population, String areaCode) {
        this.name = name;
        this.area = area;
        this.population = population;
        this.areaCode = areaCode;
      }
    
      // getter
      public String getName () {
        return name;
      }
      public double getArea () {
        return area;
      }
      public int getPopulation () {
        return population;
      }
      public String getAreaCode () {
        return areaCode;
      }
    }
    
    class ListClass {
      List<ExCity> cities = new ArrayList<>(
          Arrays.asList(
              new ExCity("Seoul", 605.2, 9720846, "02"),
              new ExCity("Incheon", 1063.3, 2947217, "032"),
              new ExCity("Ulsan", 1062, 1142190, "052"),
              new ExCity("Daegu", 883.5, 2427954, "053"),
              new ExCity("Gwangju", 501.1, 1455048, "062"),
              new ExCity("Busan", 770.1, 3404423, "051"))
      );
    }
    
    /*
    출력
    ------------------Stream 최종 연산------------------
    
    Stream List
    [Daegu, Ulsan, Incheon]
    
    Stream Map
    City : Incheon
    Incheon 1063.3 032 2947217
    City : Ulsan
    Ulsan 1062.0 052 1142190
    City : Daegu
    Daegu 883.5 053 2427954
    
    Stream Set
    Incheon 1063.3 032 2947217
    Ulsan 1062.0 052 1142190
    Daegu 883.5 053 2427954
    
    Stream Count
    6
    
    Stream forEach
    Seoul
    Incheon
    Ulsan
    Daegu
    Gwangju
    Busan
    */

8. 전체 코드

더보기
package lambdaExample;

import java.nio.file.spi.FileSystemProvider;
import java.util.*;
import java.util.stream.Collectors;
import javax.xml.transform.Source;

public class LambdaBasic2 {
  public static void main(String[] args) {
    System.out.println("\n------------------Stream 데이터 선별------------------");
    DataSelect dataSelect = new DataSelect();
    dataSelect.dataSelect();

    System.out.println("\n------------------Stream 데이터 선별2------------------");
    DataSelect2 dataSelect2 = new DataSelect2();
    dataSelect2.dataSelect2();

    System.out.println("\n------------------Stream 데이터 선별3------------------");
    DataSelect3 dataSelect3 = new DataSelect3();
    dataSelect3.dataSelect3();

    System.out.println("\n------------------Stream 데이터 개수 조절------------------");
    DataCountControl dataCountControl = new DataCountControl();
    dataCountControl.dataCountControl();

    System.out.println("\n------------------Stream 데이터 변환------------------");
    DataConversion dataConversion = new DataConversion();
    dataConversion.dataConversion();

    System.out.println("\n------------------Stream 데이터 변환2------------------");
    DataConversion2 dataConversion2 = new DataConversion2();
    dataConversion2.dataConversion2();

    System.out.println("\n------------------Stream 데이터 일치 여부------------------");
    DataMatch dataMatch = new DataMatch();
    dataMatch.dataMatch();

    System.out.println("\n------------------Stream 데이터 검색------------------");
    DataSearch dataSearch = new DataSearch();
    dataSearch.dataSearch();

    System.out.println("\n------------------Stream 데이터 연산------------------");
    DataOperation dataOperation = new DataOperation();
    dataOperation.dataOperation();

    System.out.println("\n------------------Stream 최종 연산------------------");
    FinalStream finalStream = new FinalStream();
    finalStream.finalStream();
  }
}

class FinalStream {
  public void finalStream() {
    ListClass listClass = new ListClass();

    List<String> streamList = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .sorted(Comparator.comparing(ExCity::getArea))
        .map(ExCity::getName)
        .collect(Collectors.toList());

    System.out.println("\nStream List");
    System.out.println(streamList);

    Map<String, List<ExCity>> streamMap = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .collect(Collectors.groupingBy(ExCity::getName));

    System.out.println("\nStream Map");
    streamMap.forEach((city, exCities) -> {
      System.out.println("City : " + city);
      exCities.forEach(exCity -> System.out.println(
          exCity.getName() + " " +
          exCity.getArea() + " " +
          exCity.getAreaCode() + " " +
          exCity.getPopulation()
          ));
    });

    Set<ExCity> streamSet = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .collect(Collectors.toSet());

    System.out.println("\nStream Set");
    for (ExCity exCity : streamSet) {
      System.out.println(
          exCity.getName() + " " +
          exCity.getArea() + " " +
          exCity.getAreaCode() + " " +
          exCity.getPopulation());
    }

    System.out.println("\nStream Count");
    System.out.println(listClass.cities.stream().count());

    System.out.println("\nStream forEach");
    listClass.cities.stream().map(ExCity::getName).forEach(System.out::println);
  }
}

class DataOperation {
  public void dataOperation () {
    Random random = new Random();

    int[] arr = new int[100];
    for (int i = 0; i < 100; i++) {
      arr[i] = random.nextInt(100);
    }

    int sum = 0;
    for (int num : arr) {
      sum += num;
    }

    System.out.println("일반 연산");
    System.out.println(sum);

    int streamSum = Arrays.stream(arr)
        .reduce(0, Integer::sum);

    System.out.println("스트림 연산");
    System.out.println(streamSum);
  }
}

class DataSearch {
  public void dataSearch () {
    ListClass listClass = new ListClass();

    Optional<ExCity> findAny = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 500)
        .findAny();

    System.out.println("Stream Find Any");
    System.out.println(findAny.get().getName());

    Optional<ExCity> findFirst = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 500)
        .findFirst();

    System.out.println("Stream Find First");
    System.out.println(findFirst.get().getName());
  }
}

class DataMatch {
  public void dataMatch () {
    ListClass listClass = new ListClass();

    boolean streamAnyMatch = listClass.cities.stream()
        .anyMatch(exCity -> exCity.getArea() > 1000);

    boolean streamAnyMatch2 = listClass.cities.stream()
        .anyMatch(exCity -> exCity.getArea() > 2000);

    System.out.println("Stream Any Math 1000");
    System.out.println(streamAnyMatch);
    System.out.println("Stream Any Math 2000");
    System.out.println(streamAnyMatch2);

    boolean streamAllMatch = listClass.cities.stream()
        .allMatch(exCity -> exCity.getArea() > 100);

    boolean streamAllMatch2 = listClass.cities.stream()
        .allMatch(exCity -> exCity.getArea() > 1000);

    System.out.println("Stream All Match 100");
    System.out.println(streamAllMatch);
    System.out.println("Stream All Match 1000");
    System.out.println(streamAllMatch2);

    boolean streamNoneMatch = listClass.cities.stream()
        .noneMatch(exCity -> exCity.getArea() > 1000);
    boolean streamNoneMatch2 = listClass.cities.stream()
        .noneMatch(exCity -> exCity.getArea() > 2000);

    System.out.println("Stream None Match 1000");
    System.out.println(streamNoneMatch);
    System.out.println("Stream None Match 2000");
    System.out.println(streamNoneMatch2);
  }
}

class DataConversion2 {
  public void dataConversion2 () {
    ListClass listClass = new ListClass();

    List<String> streamMap2 = listClass.cities.stream()
        .map(ExCity::getAreaCode)
        .collect(Collectors.toList());

    System.out.println("Stream Map2");
    System.out.println(streamMap2);

    List<String> streamSplit = streamMap2.stream()
        .map(areaCode -> areaCode.split(""))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());

    System.out.println("Stream Split");
    System.out.println(streamSplit);
  }
}

class DataConversion {
  public void dataConversion () {
    ListClass listClass = new ListClass();

    List<String> streamMap = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .sorted(Comparator.comparing(ExCity::getArea))
        .map(exCity -> exCity.getName())
//        .map(ExCity::getName) 위와 똑같은 역할입니다.
        .collect(Collectors.toList());

    System.out.println("Stream Map");
    System.out.println(streamMap);
  }
}

class DataCountControl {
  public void dataCountControl() {
    ListClass listClass = new ListClass();

    // limit을 이용하여 수집 개수 조절
    List<String> streamLimit = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .sorted(Comparator.comparing(ExCity::getArea))
        .map(ExCity::getName)
        .limit(2)                               // 최소 2개의 원소만 반환합니다.
        .collect(Collectors.toList());

    System.out.println("Stream Limit");
    System.out.println(streamLimit);

    // skip을 이용하여 데이터 건너뛰기
    List<String> streamSkip = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 800)
        .sorted(Comparator.comparing(ExCity::getArea))
        .map(ExCity::getName)
        .skip(1)                                      // 첫 번째 원소는 뛰어넘습니다.
        .collect(Collectors.toList());

    System.out.println("Stream Skip");
    System.out.println(streamSkip);
  }
}

class DataSelect3 {
  public void dataSelect3 () {
    ListClass listClass = new ListClass();

    // dropWhile은 true가 되면 멈추고 나머지 데이터는 버립니다.
    List<String> streamSelectAsc = listClass.cities.stream()
        .sorted(Comparator.comparing(ExCity::getArea))
        .dropWhile(exCity -> exCity.getArea() > 700)
        .map(ExCity::getName)
        .collect(Collectors.toList());

    System.out.println("dropWhile 오름차순 정렬");
    System.out.println(streamSelectAsc);

    List<String> streamSelectDesc = listClass.cities.stream()
        .sorted(Comparator.comparing(ExCity::getArea).reversed())
        .dropWhile(exCity -> exCity.getArea() > 700)
        .map(ExCity::getName)
        .collect(Collectors.toList());

    System.out.println("dropWhile 내림차순 정렬");
    System.out.println(streamSelectDesc);
  }
}

class DataSelect2 {
  public void dataSelect2 () {
    ListClass listClass = new ListClass();

    // takeWhile은 false가 되면 멈추고 나머지 데이터는 버립니다.
    List<String> streamSelectAsc = listClass.cities.stream()
        .sorted(Comparator.comparing(ExCity::getArea))
        .takeWhile(exCity -> exCity.getArea() > 700)
        .map(ExCity::getName)
        .collect(Collectors.toList());

    System.out.println("takeWhile 오름차순 정렬");
    System.out.println(streamSelectAsc);

    // area를 역순 정렬하여 비교해 보겠습니다.
    List<String> streamSelectDesc = listClass.cities.stream()
        .sorted(Comparator.comparing(ExCity::getArea).reversed())
        .takeWhile(exCity -> exCity.getArea() > 700)
        .map(ExCity::getName)
        .collect(Collectors.toList());

    System.out.println("takeWhile 내림차순 정렬");
    System.out.println(streamSelectDesc);
  }
}

class DataSelect {
  public void dataSelect () {
    ListClass listClass = new ListClass();
    listClass.cities.add(new ExCity("Seoul", 0, 0, "02"));

    System.out.println("배열 추가 시");
    for (ExCity city : listClass.cities) {
      System.out.println(
          city.getName() + " " +
          city.getArea() + " " +
          city.getPopulation() + " " +
          city.getAreaCode());
    }

    List<ExCity> streamSelect = listClass.cities.stream()
        .filter(exCity -> exCity.getArea() > 600) // 데이터 필터링
        .distinct()                           // 중복 제거
        .collect(Collectors.toList());

    System.out.println("\n중복 제거 시");
    for (ExCity city : streamSelect) {
      System.out.println(
          city.getName() + " " +
          city.getArea() + " " +
          city.getPopulation() + " " +
          city.getAreaCode());
    }
  }
}

class ExCity {
  private String name;
  private double area;     // 면적
  private int population;  // 인구
  private String areaCode; // 지역 번호

  // 필드 전체를 파라미터로 받는 생성자
  public ExCity (String name, double area, int population, String areaCode) {
    this.name = name;
    this.area = area;
    this.population = population;
    this.areaCode = areaCode;
  }

  // getter
  public String getName () {
    return name;
  }
  public double getArea () {
    return area;
  }
  public int getPopulation () {
    return population;
  }
  public String getAreaCode () {
    return areaCode;
  }
}

class ListClass {
  List<ExCity> cities = new ArrayList<>(
      Arrays.asList(
          new ExCity("Seoul", 605.2, 9720846, "02"),
          new ExCity("Incheon", 1063.3, 2947217, "032"),
          new ExCity("Ulsan", 1062, 1142190, "052"),
          new ExCity("Daegu", 883.5, 2427954, "053"),
          new ExCity("Gwangju", 501.1, 1455048, "062"),
          new ExCity("Busan", 770.1, 3404423, "051"))
  );
}

/*
출력
------------------Stream 데이터 선별------------------
배열 추가 시
Seoul 605.2 9720846 02
Incheon 1063.3 2947217 032
Ulsan 1062.0 1142190 052
Daegu 883.5 2427954 053
Gwangju 501.1 1455048 062
Busan 770.1 3404423 051
Seoul 0.0 0 02

중복 제거 시
Seoul 605.2 9720846 02
Incheon 1063.3 2947217 032
Ulsan 1062.0 1142190 052
Daegu 883.5 2427954 053
Busan 770.1 3404423 051

------------------Stream 데이터 선별2------------------
takeWhile 오름차순 정렬
[]
takeWhile 내림차순 정렬
[Incheon, Ulsan, Daegu, Busan]

------------------Stream 데이터 선별3------------------
dropWhile 오름차순 정렬
[Gwangju, Seoul, Busan, Daegu, Ulsan, Incheon]
dropWhile 내림차순 정렬
[Seoul, Gwangju]

------------------Stream 데이터 개수 조절------------------
Stream Limit
[Daegu, Ulsan]
Stream Skip
[Ulsan, Incheon]

------------------Stream 데이터 변환------------------
Stream Map
[Daegu, Ulsan, Incheon]

------------------Stream 데이터 변환2------------------
Stream Map2
[02, 032, 052, 053, 062, 051]
Stream Split
[0, 2, 3, 5, 6, 1]

------------------Stream 데이터 일치 여부------------------
Stream Any Math 1000
true
Stream Any Math 2000
false
Stream All Match 100
true
Stream All Match 1000
false
Stream None Match 1000
false
Stream None Match 2000
true

------------------Stream 데이터 검색------------------
Stream Find Any
Seoul
Stream Find First
Seoul

------------------Stream 데이터 연산------------------
일반 연산
4777
스트림 연산
4777

------------------Stream 최종 연산------------------

Stream List
[Daegu, Ulsan, Incheon]

Stream Map
City : Incheon
Incheon 1063.3 032 2947217
City : Ulsan
Ulsan 1062.0 052 1142190
City : Daegu
Daegu 883.5 053 2427954

Stream Set
Incheon 1063.3 032 2947217
Ulsan 1062.0 052 1142190
Daegu 883.5 053 2427954

Stream Count
6

Stream forEach
Seoul
Incheon
Ulsan
Daegu
Gwangju
Busan
*/

 

'JAVA' 카테고리의 다른 글

[JAVA] - Stream API 이해 6  (0) 2024.05.31
[JAVA] - Stream API 이해 5  (0) 2024.05.30
[JAVA] - Stream API 이해 3  (0) 2024.05.28
[JAVA] - Stream API 이해 2  (0) 2024.05.27
[JAVA] - Stream API 이해 1  (0) 2024.05.25