본문 바로가기
프로그래밍 언어/JAVA

JAVA - 집계 결과를 많은 순서로 정렬하기

by DGDD(Developer) 2026. 10. 16.

지난 글에서는 Map으로 언어별 표를 셌다. 개수는 구했으니 이번에는 많이 나온 항목부터 보여주는 순위를 만들어보려고 한다. 같은 표를 받은 항목은 이름순으로 놓는다.

예제 투표는 JAVA 3표, HTML과 PYTHON이 각각 2표, CSS가 1표다. 원하는 순서는 JAVA → HTML → PYTHON → CSS다.

Map에서 출력할 목록 꺼내기

HashMap의 순회 순서는 순위가 아니다. 지금 화면에서 우연히 원하는 순서로 보이더라도 정렬 기준으로 삼을 수 없다. 집계는 Map에 두고, 출력할 항목을 List로 꺼낸다.

List<Map.Entry<String, Integer>> ranking =
    new ArrayList<>(counts.entrySet());

entrySet()으로 꺼내는 Entry 하나에는 이름과 횟수가 함께 들어 있다. 이름은 getKey(), 횟수는 getValue()로 읽는다. 이 목록의 순서를 바꿔도 원래 Map이 정렬되는 것은 아니다. 출력할 때도 counts 대신 ranking을 순회해야 한다.

먼저 횟수로 비교하고 동점이면 이름 비교하기

ranking.sort(
    Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue)
        .reversed()
        .thenComparing(Map.Entry::getKey)
);

comparingInt()에는 비교할 숫자를 꺼내는 방법을 넘긴다. Map.Entry::getValue는 각 항목의 횟수를 사용한다는 뜻이다. 기본은 작은 수부터이므로 reversed()를 붙여 큰 수부터 놓았다.

그다음 thenComparing()으로 이름을 비교한다. 앞의 횟수가 같을 때만 이 기준을 쓰므로 3표인 JAVA가 먼저 오고, 2표끼리는 HTML이 PYTHON보다 앞선다. 여기서 이름순은 String의 기본 비교 순서다. 예제처럼 대문자 영문으로 맞춘 이름을 사용했다. Comparator 공식 문서

첫 줄의 <Map.Entry<String, Integer>>는 비교 대상의 타입을 명시한 부분이다. 중첩된 제네릭이 길어 보여도 비교 기준은 ‘횟수 내림차순, 동점이면 이름 오름차순’ 두 가지다.

reversed 위치를 끝으로 옮기면

다음 코드는 비슷하게 보이지만 동점의 이름까지 반대로 정렬한다.

Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue)
    .thenComparing(Map.Entry::getKey)
    .reversed()

마지막 reversed()가 그 앞에서 만든 비교 기준 전체를 뒤집기 때문이다. 이 경우 2표인 항목은 PYTHON, HTML 순서가 된다. 이번에는 횟수만 뒤집고 싶으므로 comparingInt() 바로 뒤에 두었다.

직접 비교식을 쓴다면 b.getValue() - a.getValue() 같은 뺄셈보다 Integer.compare(b.getValue(), a.getValue())가 낫다. 정수 범위를 넘는 뺄셈 때문에 비교 부호가 잘못되는 경우를 피할 수 있다.

집계부터 출력까지 연결하기

파일 이름은 클래스와 같은 VoteRanking.java다. Java 8 이상 문법을 사용했다.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class VoteRanking {
    public static void main(String[] args) {
        String[] votes = {"JAVA", "PYTHON", "HTML", "JAVA",
                          "CSS", "HTML", "PYTHON", "JAVA"};
        Map<String, Integer> counts = new HashMap<>();
        for (String vote : votes) {
            counts.put(vote, counts.getOrDefault(vote, 0) + 1);
        }

        List<Map.Entry<String, Integer>> ranking =
            new ArrayList<>(counts.entrySet());
        ranking.sort(
            Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue)
                .reversed()
                .thenComparing(Map.Entry::getKey)
        );

        for (Map.Entry<String, Integer> entry : ranking) {
            System.out.println(entry.getKey() + ": " + entry.getValue() + "표");
        }
    }
}
javac -encoding UTF-8 VoteRanking.java
java VoteRanking

예상 출력:

JAVA: 3표
HTML: 2표
PYTHON: 2표
CSS: 1표

출력에서 확인할 부분은 첫 줄만이 아니다. 2표끼리 이름순이 맞는지도 봐야 두 번째 비교 기준까지 확인할 수 있다. 빈 배열이면 출력할 줄이 없고, 한 항목만 있으면 그 항목 한 줄만 남는다.

이제 집계 결과를 일정한 순서로 보여줄 수 있다. 다음에는 여기서 상위 세 항목만 뽑아보면서 반복문과 Stream을 비교해보려고 한다.