티스토리 뷰

개념

  • 스트림에서는 스트림 요소들을 정렬할 수 있는 sorted라는 메서드를 제공한다.

  • sorted 메서드는 파라미터에 따라 2가지 버전이 존재한다.

sorted() 메서드

  • 스트림 요소의 타입에 따라 기본적으로 제공되는 정렬 방식을 사용한다.

  • NumberString 같은 경우는 오름차순, 커스텀 클래스의 경우 Comparable<T>로 구현한 방식을 따른다.

    • 커스텀 클래스의 경우 Comparable<T> 인터페이스를 구현하지 않았을 경우 최종 연산 이후에 ClassCastException이 발생할 수 있다.
  • 일반 스트림과 기본형 특화 스트림 모두에 제공되며 기본형 특화 스트림의 경우 해당 primitive 타입에 맞는 정렬 방식이 적용된다.

sorted(Comparator) 메서드

  • 스트림 요소들을 함수형 인터페이스 Comparator<? super T> comparator에서 정의한 방식으로 정렬할 수 있다.

  • Comparable<T> 인터페이스를 구현하지 않은 커스텀 클래스는 이 메서드를 사용해서 정렬 방식을 정해주어야 ClassCastException의 발생을 방지할 수 있다.

    • 기본적으로 정렬 기준이 정해져있는 클래스들의 경우 이 메서드를 사용해서 Comparator<T>를 정의한다면 기존 정렬 방식을 무시하고 새로운 정렬 방식을 적용할 수 있다.
    • NumberString 같은 경우는 이 메서드를 사용해서 내림차순으로 정렬할 수 있다.
  • 기본형 특화 스트림의 경우는 이 메서드가 제공되지 않는다.

    • 따라서 기본형 특화 스트림의 정렬 기준을 변경하고 싶다면 boxed 메서드를 통해 박싱한 후 이 메서드를 사용해서 정렬을 해야할 것 같다.

예제

  • 아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!

  • 예제 코드에서 사용되고 있는 스펙

    1. Java 15 preview (record라는 새로운 클래스 개념을 사용하기 위해서 해당 프리뷰 버전을 사용. 하위 버전의 경우는 일반 클래스를 생성한 후 getter를 만들고 사용하면 됨) - 참고
    2. JUnit 5
    3. Gradle 6.7
  • 소스 코드

기본 데이터 생성

public record Student(int no, String name, int koreanScore, int mathScore, int englishScore) implements Comparable<Student> {
    @Override
    public int compareTo(Student student) {
        int sum = koreanScore + mathScore + englishScore;
        int studentSum = student.koreanScore() + student.mathScore() + student.englishScore();
        return studentSum - sum;
    }
}
private final SortedUsage sortedUsage = new SortedUsage();
private final List<Student> students = List.of(
        new Student(1, "hong", 75, 90, 80),
        new Student(2, "sujin", 50, 90, 100),
        new Student(3, "kate", 90, 75, 75),
        new Student(4, "tae", 100, 100, 100),
        new Student(5, "lob", 75, 75, 100));

짝수 오름차순으로 정렬하기

  • NumberString 같은 자바의 기본 값으로 쓰이는 클래스들을 정렬해보자.
public List<Integer> getEvenNumbersOrderBy(List<Integer> numbers) {
    return numbers.stream()
            .filter(n -> n % 2 == 0)
            .sorted()
            .collect(Collectors.toList());
}
  • 이런 클래스들은 기본적으로 정렬 방식이 정해져 있다.
  • 가볍게 filter를 사용해서 짝수를 판별하고 sorted를 사용해서 Integer 클래스의 기본 정렬 방식(정수의 오름차순)을 사용해서 정렬을 한다.
@Test
@DisplayName("짝수 오름차순으로 정렬하기")
void getEvenNumbersOrderByTest() {
    final var numbers = List.of(5, 2, 3, 9, 4);
    final var expected = List.of(2, 4);
    final var result = sortedUsage.getEvenNumbersOrderBy(numbers);
    assertIterableEquals(expected, result);
}
  • 테스트가 성공한다.

문자열 내림차순으로 정렬하기

  • 이번에는 내림차순으로 정렬해보려고 한다.
public List<String> getStringsOrderByDesc(List<String> strings) {
    return strings.stream()
            .sorted(Collections.reverseOrder())
            .collect(Collectors.toList());
}
  • NumberString 타입의 기본 정렬 방식은 오름차순이다.

  • 하지만 내림차순이 필요한 경우가 있다.

    • 이 경우에 Comparator<T>를 파라미터로 받는 버전의 sorted 메서드를 사용한다.
    • 여기서는 Collections 클래스에서 제공하는 순서를 뒤집어주는 Collections.reverseOrder()라는 메서드를 사용했다.
  • 문자열을 내림차순(사전 역순)으로 정렬해보자.

@Test
@DisplayName("문자열 내림차순으로 정렬하기")
void getStringsOrderByDescTest() {
    final var strings = List.of("banana", "orange", "apple");
    final var expected = List.of("orange", "banana", "apple");
    final var result = sortedUsage.getStringsOrderByDesc(strings);
    assertIterableEquals(expected, result);
}
  • 테스트가 성공한다.

학생들 총점순으로 내림차순 정렬하기

  • 이번에는 커스텀으로 만든 클래스를 정렬해 보려고 한다.
public List<Student> getStudentsOrderByDesc(List<Student> students) {
    return students.stream()
            .sorted()
            .collect(Collectors.toList());
}
  • 위에서 생성한 기본 데이터 클래스인 Student 클래스는 Comparable<Student> 인터페이스를 구현하고 있다.
  • Student 클래스의 compareTo 메서드를 보면 학생들의 총점을 구해서 점수가 큰 순으로 비교할 수 있도록 구현되어 있다.
  • 따라서 Student 클래스의 기본 정렬 방식은 학생들의 총점의 내림차순이다.
@Test
@DisplayName("학생들 총점순으로 내림차순 정렬하기")
void getStudentsOrderByDescTest() {
    final var expected = List.of(
            new Student(4, "tae", 100, 100, 100),
            new Student(5, "lob", 75, 75, 100),
            new Student(1, "hong", 75, 90, 80),
            new Student(2, "sujin", 50, 90, 100),
            new Student(3, "kate", 90, 75, 75));
    final var result = sortedUsage.getStudentsOrderByDesc(students);
    assertIterableEquals(expected, result);
}
  • 테스트가 성공한다.

학생들 수학 점수순으로 내림차순 정렬하기

  • 이번에는 위에서 사용한 Student 클래스에서 정렬 방식을 바꿔서 수학 점수의 내림차순으로 정렬하려고 한다.
public List<Student> getStudentsOrderByMathScoreDesc(List<Student> students) {
    return students.stream()
            .sorted((a, b) -> b.mathScore() - a.mathScore())
            .collect(Collectors.toList());
}
  • Student 클래스의 기본 정렬 방식은 총점에 의한 내림차순이지만 sorted 메서드 부분을 살펴보면 Comparator<T>를 파라미터로 받는 버전을 사용하고 있는 것을 알 수 있다.

    • 이로 인해 기본 정렬 방식은 무시되고 내부에 람다로 구현된 Comparator<T>를 가지고 정렬 작업(수학 점수의 내림차순)을 수행한다.
  • 이렇게 클래스가 기본적으로 정렬 방식 정해져 있다고 해도 필요에 따라 메서드나 람다를 활용해서 자유롭게 정렬 방식을 변경할 수 있다.

@Test
@DisplayName("학생들 수학 점수순으로 내림차순 정렬하기")
void getStudentsOrderByMathScoreDescTest() {
    final var expected = List.of(
            new Student(4, "tae", 100, 100, 100),
            new Student(1, "hong", 75, 90, 80),
            new Student(2, "sujin", 50, 90, 100),
            new Student(3, "kate", 90, 75, 75),
            new Student(5, "lob", 75, 75, 100));
    final var result = sortedUsage.getStudentsOrderByMathScoreDesc(students);
    assertIterableEquals(expected, result);
}
  • 테스트가 성공한다.

이상 Stream API에서 사용하는 중간 연산 중 데이터 처리의 기본인 sorted 메서드에 대해 살펴보았다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
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
글 보관함