1. 개요

Java 8의 중요한 새 기능 중 하나는 Stream API 입니다. 스트림을 사용하면 배열이나 컬렉션과 같은 다양한 소스의 요소를 편리하게 처리할 수 있습니다.

또한 해당 Collectors 와 함께 Stream.collect() 메서드를 사용하여 요소를 Set , Map , List 등과 같은 다른 데이터 구조로 다시 압축할 수 있습니다 .

이 사용방법(예제)에서는 Stream  의 요소 를 TreeSet 으로 수집하는 방법을 살펴보겠습니다 .

2. 자연 순서 로 TreeSet 으로 수집

간단히 말해 TreeSet 은 정렬된 집합입니다. TreeSet 의 요소는 자연 순서 또는 제공된 Comparator 를 사용하여 정렬됩니다 .

먼저 자연 순서를 사용하여 Stream 요소 를 수집하는 방법을 살펴보겠습니다 . 그런 다음 사용자 지정 비교기 사례 를 사용하여 요소를 수집하는 데 집중하겠습니다 .

단순화를 위해 단위 테스트 어설션을 사용하여 예상한 TreeSet 결과 를 얻었는지 확인합니다 .

2.1. 문자열을 TreeSet 으로 수집

String  은 Comparable 인터페이스를 구현 하므로 먼저 String 을 예로 들어 TreeSet 에서 수집하는 방법을 살펴보겠습니다 .

String kotlin = "Kotlin";
String java = "Java";
String python = "Python";
String ruby = "Ruby";
TreeSet<String> myTreeSet = Stream.of(ruby, java, kotlin, python).collect(Collectors.toCollection(TreeSet::new));
assertThat(myTreeSet).containsExactly(java, kotlin, python, ruby);

위의 테스트에서 볼 수 있듯이 Stream 요소를 TreeSet 으로 수집하려면 TreeSet 의 기본 생성자를 Collectors.toCollection() 메서드 에 대한 메서드 참조 또는 람다 식 으로 전달 하기 만 하면 됩니다.

이 테스트를 실행하면 통과합니다.

다음으로 사용자 정의 클래스가 있는 유사한 예를 살펴보겠습니다.

2.2. 자연스러운 순서로 플레이어 수집

먼저 Player 클래스 를 살펴보겠습니다 .

public class Player implements Comparable<Player> {
    private String name;
    private int age;
    private int numberOfPlayed;
    private int numberOfWins;

    public Player(String name, int age, int numberOfPlayed, int numberOfWins) {
        this.name = name;
        this.age = age;
        this.numberOfPlayed = numberOfPlayed;
        this.numberOfWins = numberOfWins;
    }

    @Override
    public int compareTo(Player o) {
        return Integer.compare(age, o.age);
    }

    // getters are omitted
}

위의 클래스에서 볼 수 있듯이 Player 클래스는 Comparable 인터페이스를 구현합니다. 또한 compareTo() 메서드에서 플레이어의 나이 로 자연 순서를 정의했습니다 .

다음으로 몇 가지 Player 인스턴스를 만들어 보겠습니다.

/*                          name  |  age  | num of played | num of wins
                           --------------------------------------------- */
Player kai = new Player(   "Kai",     26,       28,            7);
Player eric = new Player(  "Eric",    28,       30,           11);
Player saajan = new Player("Saajan",  30,      100,           66);
Player kevin = new Player( "Kevin",   24,       50,           49);

나중에 다른 데모에서 이 네 가지 플레이어 개체를 사용할 것이므로 각 플레이어의 속성 값을 쉽게 확인할 수 있도록 테이블과 같은 형식으로 코드를 넣습니다.

이제 자연스러운 순서로 TreeSet 에서 수집 하고 예상 결과가 있는지 확인합니다.

TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin).collect(Collectors.toCollection(TreeSet::new));
assertThat(myTreeSet).containsExactly(kevin, kai, eric, saajan);

보시다시피 코드는 문자열을 TreeSet 으로 수집하는 것과 매우 유사합니다 . PlayercompareTo()  메서드는 "나이" 속성을 자연 순서로 지정 했기 때문에 연령 오름차순으로 정렬된 플레이어로 결과( myTreeSet )를 확인합니다.

우리는 AssertJcontainsExactly() 메서드를 사용하여 TreeSet 이 순서대로 주어진 요소를 정확하게 포함 하고 있는지 확인했습니다 .

다음으로, 사용자 정의된 Comparator 를 사용하여 이러한 플레이어를 TreeSet 으로 수집하는 방법을 살펴보겠습니다 .

3. Custom형 비교기 를 사용하여 TreeSet 으로 수집

Collectors.toCollection(TreeSet::new) 을 사용하면 Stream 의 요소를 자연스러운 순서대로 TreeSet 으로 수집할 수 있습니다. TreeSetComparator 객체를 인수로 받아들이는 또 다른 생성자를 제공합니다 .

public TreeSet(Comparator<? super E> comparator) { ... }

따라서 TreeSet 이 요소에 다른 순서를 적용하도록 하려면 Comparator 개체 를 만들어 위에서 언급한 생성자에 전달할 수 있습니다 .

다음으로, 연령 대신 승리 횟수별로 TreeSet 에서 이러한 플레이어를 수집해 보겠습니다 .

TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin)
  .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparingInt(Player::getNumberOfWins))
));
assertThat(myTreeSet).containsExactly(kai, eric, kevin, saajan);

이번에는 람다 식을 사용하여 TreeSet 인스턴스를 생성했습니다. 또한 Comparator.comparingInt()  를 사용 하여 TreeSet 의 생성자 에 자체 Comparator 를 전달했습니다.

Player::getNumberOfWins 는 플레이어를 비교하는 데 필요한 속성 값을 참조합니다.

실행하면 테스트가 통과됩니다.

그러나 필요한 비교 논리는 예에서처럼 속성 값을 비교하는 것만큼 단순하지 않은 경우가 있습니다. 예를 들어 몇 가지 추가 계산 결과를 비교해야 할 수 있습니다.

마지막으로 이러한 플레이어를 TreeSet 에 다시 수집해 보겠습니다. 하지만 이번에는 승률(승수/플레이 횟수)에 따라 정렬하기를 원합니다.

TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin)
  .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(player -> BigDecimal.valueOf(player.getNumberOfWins())
    .divide(BigDecimal.valueOf(player.getNumberOfPlayed()), 2, RoundingMode.HALF_UP)))));
assertThat(myTreeSet).containsExactly(kai, eric, saajan, kevin);

위의 테스트에서 볼 수 있듯이 Comparator.comparing (Function keyExtractor) 메서드를 사용하여 비교 가능한 정렬 키를 지정했습니다 . 이 예에서 keyExtractor 함수는 플레이어의 승률을 계산하는 람다 식입니다.

또한 테스트를 실행하면 통과합니다. 따라서 예상되는 TreeSet 이 있습니다.

4. 결론

이 기사에서는 자연 순서 및 사용자 정의 비교기에 의해 Stream  의 요소 를 TreeSet 으로 수집하는 방법을 예제를 통해 논의했습니다 .

항상 그렇듯이 예제의 전체 소스 코드는 GitHub에서 사용할 수 있습니다.

Generic footer banner