Study/Java

자바 기본서를 다시 읽다. 9 - 스트림의 최종처리 메소드

going.yoon 2022. 4. 6. 22:28

스트림의 최종 처리 메소드에 대해서 알아보자.

 

매칭(allMatch(), anyMatch(), noneMatch())

매칭 메소드는 최종처리시 요소들이 특정 조건에 만족하는지 Predicate<T> 를 사용하여 조사한다. 어려울것이 없다.

public static void main (String[] args){

    int[] intArr = new int[]{1,3, 5};

    boolean isAllEvenNum = Arrays.stream(intArr).allMatch(a->a%2==0);
    boolean isThereEvenNum = Arrays.stream(intArr).anyMatch(a->a%2==0);
    boolean isAllOddNum = Arrays.stream(intArr).noneMatch(a->a%2==0);

    System.out.println("isAllEvenNum : " + isAllEvenNum ); // false
    System.out.println("isThereEvenNum : " + isThereEvenNum ); // false
    System.out.println("isAllOddNum : " + isAllOddNum ); // true
}

 

 

 

기본집계(sum(), count(), average(), max(), min())

스트림이 제공하는 기본 집계 함수들은 그 내용이 어려울 것은 없지만, 주의할 것이 있다. sum()을 제외하고는 집계 메소드들의 리턴값이 int, long, double 과 같은 primitive type이 아니라 Optional 클래스라는 것이다. 이 객체에서 값을 얻기 위해서는 get(), getAsDouble(), getAsLong()을 호출해주어야 한다.

average() 중간처리 이후 호출된 getClass() -> Optional 객체임을 확인

public static void main (String[] args){

    int[] intArr = new int[]{1,3, 5};
    System.out.println(Arrays.stream(intArr)
            .average()
            .getAsDouble()); // print 3.0

    int first = Arrays.stream(intArr)
            .filter(s->s%3==0)
            .findFirst() // 가장 첫번째 3의 배수를 찾아라
            .getAsInt();
    System.out.println(first); // print 3

}

 

 

Optional Class

optional class들은 단순히 집계값을 저장하는 것 뿐 아니라 , Default값을 설정 할 수도 있고 집계값을 처리하는 Consumer를 등록할수도 있다. 만약 리스트가 선언만 되어있고 값이 저장되어있지 않다고 가정해보자. 그러면 해당 리스트에 stream의 average 메소드를 호출하면 java.util.NoSuchElementException과 같은 에러가 발생할 것이다. 이러한 경우 사용할 수 있는게 Optional Class이다.

List<Integer> list = new ArrayList<>(); // list에 아직 값이 없는 상태

double avg = list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .getAsDouble(); // 해당 라인에서 에러 발생

NoSuchElementException : No value present

 

위와 같은 사례는 아래와 같이 Optional 객체를 사용하여 해결할 수 있다.

public static void main (String[] args){

    List<Integer> list = new ArrayList<>(); // list에 아직 값이 없는 상태

    System.out.println("*****Sol using optional.isPresent() ");
    OptionalDouble optional = list.stream()
            .mapToInt(Integer::intValue)
            .average();
    if( optional.isPresent() ) System.out.println("optional.getAsDouble() : " + optional.getAsDouble());
    else System.out.println("default : "+ 0.0 + "\n");


    System.out.println("*****Sol using orElse");
    double avg = list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .orElse(0.0);
    System.out.println("avg is : " + avg + "\n");

    System.out.println("***** sol using ifPresent lamda");
    list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .ifPresent(a-> System.out.println("avg is : " + a))
    ;

}

실행결과. ifPresent lamda sol은 아예 출력이 안된다.

 

커스텀 집계 reduce()

스트림은 사용자가 집계를 커스터마이징하여 사용할 수 있도록 reduce 메소드를 제공한다. 이 reduce 메소드의 특징은 스트림에 요소가 없을 경우 default로 사용할 수 있도록 identity 매개값을 설정해줄 수 있다는 것이다. 그리고 이 identity와 함께 매개값으로 Operator 인터페이스를 넘겨주어야 한다.

public static class Car {
    public String name;
    public int price;

    public Car (String name, int price){
        this.name =name;
        this.price = price;
    }

    public int getPrice() { return this.price ; }
}
public static void main (String[] args){

    List<Car> carList = Arrays.asList(
            new Car("차 1" , 15000),
            new Car("차 2" , 20000)
    );

    int Sum = carList.stream()
    .map(Car::getPrice)
    .reduce(0, (a,b) -> a+b );
    System.out.println("sum is : " + Sum); // print 35000

}