IT/JAVA | Spring

[JAVA] 특수문자 > 숫자(0123~순, 소수점 포함) > 영어(대문자 먼저, abc~순) > 한글(ㄱㄴㄷ~순) 순서 Custom Comparator로 String 정렬

으어으어 2023. 1. 28. 18:01

해당 util은 아래 블로그를 참조하여 만들었습니다.

이미 잘 만들어진 util에 제가 필요로하는 기능을 위한 추가, 수정만 진행하였습니다.

출처는 아래에서 확인 가능합니다.

 

한글-영어-특수문자 순 정렬하는 java compare 메서드 만들기

카톡을 보게 되면, 한글 -> 영문 -> 특수문자 순으로 정렬을 합니다. 지난 번 프로젝트 하면서 동일한 요구사항이 있었는데, 생각보다 머리아프더라고요. 그냥 문자열순으로 정렬하면 그 순서가

www.reimaginer.me

 

저제가 작성한 util은 Compartor의 compare를 override하여 구현했습니다.

 

특수문자, 한글을 포함하여 정렬하기 위한 Custom Comparator 소스

import java.util.Comparator;
import org.apache.commons.lang3.math.NumberUtils;

public class StringComparator implements Comparator<String> {
    /**
     * 특수문자 > 숫자(0123순) > 영어(대소문자 구분, 대문자 먼저, abc순) > 한글(ㄱㄴㄷ순) 순서 정렬
     * <br>정렬 시 공백은 제외하고 정렬한다.
     * <br>해당 util은 reimaginer 블로그를 참조하여 만들어졌습니다.
     * <br>블로그 링크는 아래 참조
     * @param left - 비교할 첫 문자
     * @param right - 비교할 두번째 문자
     * @throws NullPointerException – 인수가 null이고 이 비교기가 null 인수를 허용하지 않는 경우
     * @throws ClassCastException – 인수의 유형이 이 비교기에 의해 비교되지 않는 경우.
     * @return 음수,0,양수
     */
    @Override
    public int compare(String left, String right) {
        left = left.replace(" ", "");
        right = right.replace(" ", "");

        // 비교 대상문자가 모두 숫자일떄
        if (!left.startsWith("0") && !right.startsWith("0")
            && NumberUtils.isParsable(left) && NumberUtils.isParsable(right)) {
            return makeSortedByNumber(left, right);
        }

        // 비교 대상문자가 둘중 하나라도 숫자가 아닐때
        int leftLen = left.length();
        int rightLen = right.length();
        int minLen = Math.min(leftLen, rightLen);

        for (int i = 0; i < minLen; ++i) {
            char leftChar = left.charAt(i);
            char rightChar = right.charAt(i);

            if (leftChar != rightChar) {
                return makeSortedByNotNumber(leftChar, rightChar);
            }
        }

        return leftLen - rightLen;
    }

    private int makeSortedByNumber(String left, String right) {
        double leftDouble = Double.parseDouble(left);
        double rightDouble = Double.parseDouble(right);
        if (leftDouble > rightDouble) {
            return (int) Math.ceil(leftDouble - rightDouble);
        } else {
            return (int) Math.ceil(rightDouble - leftDouble) * -1;
        }
    }

    private int makeSortedByNotNumber(char leftChar, char rightChar) {
        // 특수문자가 있는 경우
        if (isAnyMatchSpecial(leftChar, rightChar)) {
            if (isEnglish(leftChar) || isNumber(leftChar) || isKorean(leftChar)) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return leftChar - rightChar;
        }
    }

    private boolean isAnyMatchSpecial(char ch1, char ch2) {
        return isEnglishAndSpecial(ch1, ch2)
            || isNumberAndSpecial(ch1, ch2)
            || isKoreanAndSpecial(ch1, ch2);
    }
    private boolean isKoreanAndSpecial(char ch1, char ch2) {
        return (isKorean(ch1) && isSpecial(ch2))
            || (isSpecial(ch1) && isKorean(ch2));
    }

    private boolean isEnglishAndSpecial(char ch1, char ch2) {
        return (isEnglish(ch1) && isSpecial(ch2))
            || (isSpecial(ch1) && isEnglish(ch2));
    }

    private boolean isNumberAndSpecial(char ch1, char ch2) {
        return (isNumber(ch1) && isSpecial(ch2))
            || (isSpecial(ch1) && isNumber(ch2));
    }

    public boolean isEnglish(char ch) {
        return (ch >=  'A' && ch <=  'Z')
            || (ch >=  'a' && ch <=  'z');
    }

    public boolean isKorean(char ch) {
        return ch >= Integer.parseInt("AC00", 16)
            && ch <= Integer.parseInt("D7A3", 16);
    }

    public boolean isNumber(char ch) {
        return ch >= '0' && ch <= '9';
    }

    public boolean isSpecial(char ch) {
        return (ch >= '!' && ch <= '/') // !"#$%&'()*+,-./
            || (ch >= ':' && ch <= '@') //:;<=>?@
            || (ch >= '[' && ch <= '`') //[\]^_`
            || (ch >= '{' && ch <= '~'); //{|}~
    }
}

 

사용 방법

List<OrderDto> orderDtos = new ArrayList<>();
List<String> testArray = Arrays.asList("1234", "832", "123.123", "832.523",
	"한글", "가나다라","hello", "check", "ch_ck", "[중괄호]", "[eng]", "{대괄호}",
	"1623", "넘버아님", "/특수/", "832", "가나다라1", "Aad", "Check",
	"634.123", "634.31123", "01234", "08561", "<eng>", "^test^",
	"&대괄호&", "#shap]", "#java]", "#대괄호}"
);

for (int i = 0; i < testArray.size(); i++) {
	OrderDto orderDto = new OrderDto();
	orderDto.setNum(i);
	orderDto.setName(testArray.get(i));
	orderDto.setInfo("testArray index : " + i);
	orderDtos.add(orderDto);
}

List<String> orderResult = orderDtos.stream()
            .sorted((o1, o2) -> new StringComparator().compare(o1.getName(), o2.getName()))
            .map(OrderDto::getName)
            .collect(Collectors.toList());

 

결과

#java], #shap], #대괄호}, &대괄호&, /특수/, <eng>, [eng], [중괄호], ^test^, {대괄호}, 01234, 08561, 123.123, 634.123, 634.31123, 832, 832, 832.523, 1234, 1623, Aad, Check, ch_ck, check, hello, 가나다라, 가나다라1, 넘버아님, 한글