Stream 클래스의 sorted 메서드
Stream의 sorted와 Comparator의 comparingInt
Stream 클래스의 sorted 메서드와 Comparator의 comparingInt 코드를 까보면서 stream과 generic 그리고 lambda를 더 깊이 공부하는 시간을 가졌습니다.
살펴 볼 부분
- Stream의 sorted메서드
Stream<T> sorted(Comparator<? super T> comparator);
- Comparator의 comparingInt메서드
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
- Comparator Functional Interface
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
~~~
}
예제 코드
두 메서드를 이용하여 간단한 예제를 만들어 보겠습니다.
음료 클래스가 있고, 음료 리스트를 만들었습니다. 음료 리스트를 가격 기준으로 오름차순으로 정렬합니다.
public class GenericWildCardExample {
public static void main(String[] args) {
List<Beverage> beverages = Arrays.asList(new Beverage(3000), new Beverage(2000), new Beverage(10000), new Beverage(4000));
List<Beverage> sortedBeverages = beverages.stream().sorted(Comparator.comparingInt(Beverage::getPrice))
.collect(toList());
for (Beverage sortedBeverage : sortedBeverages) {
System.out.println("sortedBeverage.getPrice() = " + sortedBeverage.getPrice());
}
}
}
class Beverage{
private int price;
public Beverage(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
}
결과 출력
sortedBeverage.getPrice() = 2000
sortedBeverage.getPrice() = 3000
sortedBeverage.getPrice() = 4000
sortedBeverage.getPrice() = 10000
코드 안으로
가장 먼저 볼 곳은 Stream의 sorted()입니다. sorted 매서드의 매개변수를 보면 Comparator<? super T> comparator임을 확인할 수 있습니다. 즉 T 또는 T의 부모 클래스를 Generic 타입으로 가지는 Comparator를 매개변수로 받고 있습니다.
Stream<T> sorted(Comparator<? super T> comparator);
(이 코드가 어렵다면 Generic - WildCard를 공부해보세요)
이제 Comparator를 살펴보겠습니다.
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
~~~
}
Comparator는 함수형 인터페이스입니다. 이 코드를 읽다 궁금한 점이 생겼습니다.
함수형 인터페이스에 왜 추상 메서드가 2개나 있지..??
여기서 equals는 사실 Object 클래스의 메서드입니다. 따라서 Comparator 인터페이스의 추상 메서드는 compare하나입니다.
참고) https://stackoverflow.com/questions/23721759/functionalinterface-comparator-has-2-abstract-methods
다시 본론으로 돌아가겠습니다.
sorted 메서드의 매개변수는 Comparator이고 Comparator는 함수형 인터페이스이므로 sorted 메서드의 매개변수는
(T o1, T o2) -> {return int a}형태의 람다식이 되어야합니다.
그렇다면 예제 코드에서 sorted안에 Comparator.comparingInt는 어떻게 sorted의 인자로 들어갈 수 있었을까요?
List<Beverage> sortedBeverages = beverages.stream().sorted(Comparator.comparingInt(Beverage::getPrice))
.collect(toList());
Comparator.comparingInt의 반환형을 살펴보면 그 이유를 알 수 있습니다.
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
comparingInt를 보면 return 값으로 (c1, c2) -> {return int}형태의 람다식을 반환함을 알 수 있습니다.
그렇다면 comparingInt의 매개변수인 ToIntFunction은 어떤 모습이길래 Beverage::getPrice를 매개 변수로 받을 수 있는 것일까요?
comparingInt의 매개변수인 ToIntFuntion를 살펴보겠습니다.
@FunctionalInterface
public interface ToIntFunction<T> {
/**
* Applies this function to the given argument.
*
* @param value the function argument
* @return the function result
*/
int applyAsInt(T value);
}
ToIntFunction은 매개 변수 하나를 받아서 int를 반환하는 추상 메서드를 가지고 있는 함수형 인터페이스입니다.
따라서 예제 코드에 있는 comparingInt()는 매개변수의 형태에 맞는 메서드 레퍼런스인 Beverage::getPrice()를 인자로 받아서 (c1, c2) -> {return int}형태의 람다식을 반환합니다.
그리고 sorted()는 comparingInt가 반환해준 람다식을 인자로 하여 stream을 정렬해 주는 것을 확인할 수 있습니다.
List<Beverage> sortedBeverages = beverages.stream().sorted(Comparator.comparingInt(Beverage::getPrice))
.collect(toList());
더 알아보고 싶은 것
- comparingInt가 람다식을 반환할 때 (Comparator & Serializable)는 왜 붙여주는 걸까?
- 직렬화
- Stream의 sorted 메서드는 어디에 구현되어있고, 내부적으로 어떻게 동작하는걸까?
Author And Source
이 문제에 관하여(Stream 클래스의 sorted 메서드), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@ohjinhokor/코드-안으로-Stream-클래스의-sorted-메서드저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)