본문 바로가기

Java

Java8 이후 람다를 이용하여 Collection 정렬하기 (정렬을 깔끔하게 작성하는 법)

원문 : https://www.baeldung.com/java-8-sort-lambda

 

우선 아래와 같은 class가 있다고 했을때

public class Human {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

 

Java8 이전에는 아래 코드와 같이 Comparator를 구현하여 정렬에 사용했다.

void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = new ArrayList<>();
    humans.add(new Human("Sarah", 10));
    humans.add(new Human("Jack", 12));

    Collections.sort(humans, new Comparator<Human>() {
        @Override
        public int compare(Human h1, Human h2) {
            return h1.getName().compareTo(h2.getName());
        }
    });

    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

 

이랬던 코드를 Java8에서 람다가 등장한 이후로 아래와 같이 작성이 가능해 졌다.

void whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = new ArrayList<>();
    humans.add(new Human("Sarah", 10));
    humans.add(new Human("Jack", 12));

    // humans.sort((Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));
    humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

 

위 코드는 다시 Comparator에서 static 으로 제공하는 comparing() 함수와 메소드 참조를 이용해서 아래와 같이 작성할 수 있다. comparing() 함수의 파라미터는 Comparable 의 구현체 이어야 한다.

void givenInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = Arrays.asList(
            new Human("Sarah", 10),
            new Human("Jack", 12)
    );

    // Collections.sort(humans, Comparator.comparing(Human::getName));
    humans.sort(Comparator.comparing(Human::getName));
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

 

만약 정렬 순서를 변경하고 싶다면, Comparator에 reversed() 함수를 이용한다.

humans.sort(Comparator.comparing(Human::getName).reversed());

 

이름순, 나이순 정렬 처럼 두개 이상의 정렬 인자를 적용하고 싶다면 아래처럼 thenComparing() 함수를 이용한다.

humans.sort(
        Comparator.comparing(Human::getName).thenComparing(Human::getAge)
);

 

이상의 방법은 collection 의 sort함수를 이용하기때문에 sort를 수행하고 나면 원래 collection 의 순서가 변경된다.
이를 방지하고 싶다면 stream 을 이용해서 정렬을 수행하고 원본 collection 과 별도의 정렬된 collection 을 생성하면 된다.

우선 Comparable을 구현한 타입이라면 아래 처럼 stream().sorted() 함수를 이용하면된다.

void givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<String> letters = Arrays.asList("B", "A", "C");

    List<String> sortedLetters = letters.stream().sorted().collect(Collectors.toList());
    assertThat(sortedLetters.get(0), equalTo("A"));
}

 

Comparator.comparing() 함수를 사용할 수도 있다.

void givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List<Human> humans = Arrays.asList(new Human("Sarah", 10), new Human("Jack", 12));

    List<Human> sortedHumans = humans.stream()
            .sorted(Comparator.comparing(Human::getName))
            .collect(Collectors.toList());

    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
    assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

 

Comparator을 사용하기 때문에 위에서 봤었던, reversed()함수나 thenComparing() 함수도 사용 할 수 있다.

Collection 에 null 값이 포함되는 경우 Comparator.nullsFirst(), Comparator.nullsLast() 함수를 이용해서 null 값을 colleciton 맨 앞이나 뒤로 오게 설정 할 수도 있다.

void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToLast() {
    List<Human> humans = Arrays.asList(null, new Human("Jack", 12), null);

    humans.sort(Comparator.nullsLast(Comparator.comparing(Human::getName)));

    assertNotNull(humans.get(0));
    assertNull(humans.get(1));
    assertNull(humans.get(2));
}


void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToStart() {
    List<Human> humans = Arrays.asList(null, new Human("Jack", 12), null);

    humans.sort(Comparator.nullsFirst(Comparator.comparing(Human::getName)));

    assertNull(humans.get(0));
    assertNull(humans.get(1));
    assertNotNull(humans.get(2));
}

 

반응형