Stream API의 활용 및 사용법 (최종연산)-(5)

윤재열·2022년 11월 5일
0

Java

목록 보기
64/71
post-custom-banner

Stream 결과 만들기 (최종 연산)

최종 연산을 통해 생성된 Stream을 바탕으로 결과를 만ㄷ르어야 중간연산한 결과값들을 뽑아낼 수 있습니다.

최댓값, 최곳값, 총합, 평균 , 갯수 [Max,Min,Sum,Average,Count]

  • Stream의 요소들을 대상으로 최솟값이나 최댓값 또는 총합을 구하기 위한 최종 연산들이 존재합니다.
  • 최소값이나 최대값을 구하기 위해서는 max,min을 이용해야 합니다.
  • 총합,평균,개수를 구하기 위해서는 sum,average,count를 이용해야 합니다.
  • min,max,average는 Stream이 비어있는 경우에 값을 특정할 수가 없습니다. 그렇기 때문에 다음과 같이 Optional로 값이 반환됩니다.
OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();
int max = IntStream.of().max().orElse(0);
IntStream.of(1, 3, 5, 7, 9).average().ifPresent(System.out::println);
  • 반면에 총합이나 갯수의 경우에는 값이 비어있는 경우 0으로 값을 특정할 수 있습니다. 그렇기 때문에 Steram API는 sum, count 메서드에 대해 Optional이 아닌 원시 값을 반환하도록합니다.당연히 Stream이 비어있을 때는 0을 반환하게 됩니다.
long count = IntStream.of(1, 3, 5, 7, 9).count();
long sum = LongStream.of(1, 3, 5, 7, 9).sum();

데이터 수집[collect]

  • Stream 요소들을 List,Set,Map 등 다른 결과로 수집하고 싶은 경우에는 collect 함수를 이용할 수 있습니다.

  • collect 함수는 어떻게 Stream의 요소들을 수집할 것인가를 정의한 Collector 타입을 인자로 받아서 처리합니다.

  • 일반적으로 List로 Stream의 요소들을 수집하는 경우가 많은데, 이렇게 자주 사용하는 작업은 Collectors 객체에서 static 메서드로 제공하고 있습니다.

  • 만약 원하는 것이 없는 경우에는 Collector 인터페이스를 직접 구현하여 사용할 수도 있습니다.

  • 예를들어 사용해 봅니다.Product객체는 수량(amount)와 이름(name)을 변수로 가지며, 주어진 데이터를 다양한 방식으로 수집해볼 것입니다.

List<Product> productList = Arrays.asList(
	new Product(23, "potatoes"),
	new Product(14, "orange"),
	new Product(13, "lemon"),
	new Product(23, "bread"),
	new Product(13, "sugar"));

Collectors.toList()

  • Stream에서 작업한 결과를 List로 반환받을 수 있습니다.
  • 아래는 Stream의 요소들을 Product의 이름으로 반환하여, 그결과를 List로 반환받고 있습니다.
List<String> nameList = productList.stream()
			.map(Product::getName)
            .collect(Collectors.toList());
  • 만약 해당결과를 List가 아닌 Set으로 받고 싶다면
    .collect(Collectors.toSet()); 으로 사용하면 됩니다.

Collectors.joining()

  • Stream 에서 작업한 결과를 1개의 String으로 이어붙이기를 원하는 경우에는 Collectors.joining()을 이용 할 수 있습니다.
  • Collectors.joining()은 총 3개의 인자를 받을 수 있습니다.
    • delimiter : 각 요소 중간에 들어가 요소를 구분시켜주는 구분자
    • prefix : 결과 맨 앞에 붙는 문자
    • suffix : 결과 맨 뒤에 붙는 문자
String listToString = productList.stream()
	.map(Product::getName)
	.collect(Collectors.joining());
// potatoesorangelemonbreadsugar

String listToString = productList.stream()
	.map(Product::getName)
	.collect(Collectors.joining(" "));
// potatoes orange lemon bread sugar

String listToString = productList.stream()
  	.map(Product::getName)
  	.collect(Collectors.joining(", ", "<", ">"));
// <potatoes, orange, lemon, bread, sugar>

Collectors.averageInt(), Collectors.summingInt(), Collectors.summarizingInt()

  • Stream에서 작업한 결과의 평균값이나 총합 등을 구하기 위해서는 Collectors.averagingInt()와 Collectors.summingInt()를 이용할 수 있습니다.
Double averageAmount = productList.stream()
	.collect(Collectors.averagingInt(Product::getAmount));

// 86
Integer summingAmount = productList.stream()
	.collect(Collectors.summingInt(Product::getAmount));

// 86
Integer summingAmount = productList.stream()
    .mapToInt(Product::getAmount)
    .sum();
  • 하지만 만약 1개의 Stream으로부터 개수,합계,평균,최대값,최소값을 한번에 얻고 싶은 경우에는 어떻게 하는지 공부해봅니다.
  • 동일한 Stream 작업을 여러 번 실행하는 것은 그렇게 좋지 못한 방법이기 때문에 , 이러한 경우에는 Collectors.summaringIng()를 이용하는것이 좋습니다.
    • 이를 이용하면 intSummaryStatistics 객체가 반환되며, 필요한 값에 대해 get 메서드를 이용하여 원하는 값을 꺼내주면 됩니다.
    • 개수: getCount()
    • 합계: getSum()
    • 평균: getAverage()
    • 최소: getMin()
    • 최대: getMax()
IntSummaryStatistics statistics = productList.stream()
    .collect(Collectors.summarizingInt(Product::getAmount));

//IntSummaryStatistics {count=5, sum=86, min=13, average=17.200000, max=23}

Collectors.groupingBy()

  • Stream에서 작업한 결과를 특정 그룹으로 묶기를 원할 수도 있습니다. 이러한 경우에는 Collectors.groupingBy() 함수를 이용할 수 있으며 결과는 Map으로 받게 됩니다.
  • groupingBy()는 매개변수로 함수형 인터페이스 Function을 필요로 합니다.
  • 예를 들어 수량을 기준으로 grouping을 원하는 경우에 아래와 같이 작성할 수 있으며, 같은 수량일 경우에는 List로 묶어서 값을 반환하게 됩니다.
Map<Integer, List<Product>> collectorMapOfLists = productList.stream()
  .collect(Collectors.groupingBy(Product::getAmount));

/*
{23=[Product{amount=23, name='potatoes'}, Product{amount=23, name='bread'}], 
 13=[Product{amount=13, name='lemon'}, Product{amount=13, name='sugar'}], 
 14=[Product{amount=14, name='orange'}]}
 */

Collectors.partitioningBy()

  • Collectors.groupBy()가 함수형 인터페이스 Function을 사용해서 특정 값을 기준으로 stream 내의 요소들을 그루핑 하였다면, Collectors.partitioiningBy()는 함수형 인터페이스 Predicate를 받아 Boolean 을 Key 값으로 partitioning합니다.
  • 예를 들어 제품의 개수가 15보다 큰 경우와 그렇지 않은 경우를 나누고자 한다면 다음과 같이 코드를 작성할 수 있습니다.
Map<Boolean, List<Product>> mapPartitioned = productList.stream()
	.collect(Collectors.partitioningBy(p -> p.getAmount() > 15));

/*
{false=[Product{amount=14, name='orange'}, Product{amount=13, name='lemon'}, Product{amount=13, name='sugar'}], 
 true=[Product{amount=23, name='potatoes'}, Product{amount=23, name='bread'}]}
 */

조건검사 [Match]

  • Stream 요소들이 특정한 조건을 충족하는지 검사하고 싶은 경우에는 match 함수를 이용할 수 있습니다.

  • match 함수는 함수형 인터페이스 Predicate를 받아서 해당 조건을 만족하는지 검사를 하게 되고, 검사결과를 boolean으로 반환합니다.

  • match 함수의 3가지

    • anyMatch : 1개의 요소라도 해당 조건을 만족하는가
    • allMatch : 모든 요소가 해당조건을 만족하는가
    • nonMatch: 모든 요소가 해당 조건을 만족하지 않는가
  • 예를 들어 다음과 같은 예시 코드가 있다고 할때 아래의 경우 모두 true를 반환하게 됩니다.

List<String> names = Arrays.asList("Eric", "Elena", "Java");

boolean anyMatch = names.stream()
    .anyMatch(name -> name.contains("a"));
boolean allMatch = names.stream()
    .allMatch(name -> name.length() > 3);
boolean noneMatch = names.stream()
    .noneMatch(name -> name.endsWith("s"));

특정 연산 수행 [forEach]

  • Stream 요소들을 대상으로 어떤 특정한 연산을 수행하고 싶은 경우에는 forEach 함수를 이용할 수 있습니다.
  • 앞에서 살펴본 비슷한 함수로 peek()가 있습니다..
  • peek()는 중간연산으로써 실제 요소들에 영향을 주지 않은 채로 작업을 진행하고, Stream을 바노한하는 함수입니다.
  • 하지만 forEach()는 최종 연산으로써 실제 요소들에 영향을 줄 수 있으며, 반환값이 존재하지 않습니다.
  • 예를들어 요소들을 출력하기를 원할 떄 다음과 가이 forEach()를 사용할 수 있습니다.
names.stream()
    .forEach(System.out::println);
profile
블로그 이전합니다! https://jyyoun1022.tistory.com/
post-custom-banner

0개의 댓글