ITEM 14: Consider implementing comparable

public interface Comparable<T> {
    /**
     * ์ด๊ฐ์ฒด์™€ ์ฃผ์–ด์ง„ ๊ฐ์ฒด์˜ ์ˆœ์„œ๋ฅผ ๋น„๊ต
     * ๊ฐ์ฒด๊ฐ€ ์ฃผ์–ด์ง„ ๊ฐ์ฒด๋ณด๋‹ค ์ž‘์œผ๋ฉด ์Œ์˜ ์ •์ˆ˜
     * ๊ฐ™์œผ๋ฉด 0
     * ํฌ๋ฉด ์–‘์˜ ์ •์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
     * ๋น„๊ตํ•  ์ˆ˜ ์—†๋Š” ํƒ€์ž…์˜ ๊ฐ์ฒด๊ฐ€ ์ฃผ์–ด์ง€๋ฉด ClassCastException
     */
    public int compareTo(T o);
}

Comparable ์€ ๋‹จ์ˆœ ๋™์น˜์„ฑ ๋น„๊ต์™€ ์ˆœ์„œ ๋น„๊ต๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” Generic ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. Comparable์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์—๋Š” ์ž์—ฐ์ ์ธ ์ˆœ์„œ๊ฐ€ ์žˆ์Œ์„ ๋œปํ•˜๋ฉฐ, Comparable์„ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋“ค์˜ ๋ฐฐ์—ด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‰ฝ๊ฒŒ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

Arrays.sort(a);
// String์€ Comparable์„ ๊ตฌํ˜„ํ•จ
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
  ...
}

์‚ฌ์‹ค์ƒ ์ž๋ฐ” ํ”Œ๋žซํผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ชจ๋“  ๊ฐ’ ํด๋ž˜์Šค์™€ ์—ด๊ฑฐํƒ€์ž…์ด Comparable์„ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ, ์•ŒํŒŒ๋ฒณ, ์ˆซ์ž, ์—ฐ๋Œ€ ๊ฐ™์ด ์ˆœ์„œ๊ฐ€ ๋ช…ํ™•ํ•œ ๊ฐ’ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋ฉด Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

compareTo ๋ฉ”์„œ๋“œ ์ผ๋ฐ˜ ๊ทœ์•ฝ

sgn์€ signum function์„ ๋œปํ•˜๊ณ , ํ‘œํ˜„์‹์˜ ๊ฐ’์ด ์Œ์ˆ˜, 0, ์–‘์ˆ˜์ผ ๋•Œ -1, 0, 1์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ •์˜

  1. sgn(x.compareTo(y)) == -sgn(y.compareTo(x))

  2. x.compareTo(y) > 0 && y.compareTo(z) > 0์ด๋ฉด x.compareTo(z) > 0 ์ด๋‹ค.

  3. x.compareTo(y) == 0 ์ด๋ฉด sgn(x.compareTo(z)) == sgn(y.compareTo(z)) ์ด๋‹ค.

  4. (x.compareTo(y) == 0 ) == (x.equals(y)) ์—ฌ์•ผํ•œ๋‹ค. (์ด ๊ถŒ๊ณ ๋Š” ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ ๊ผญ ์ง€ํ‚ค๋Š”๊ฒŒ ์ข‹์œผ๋ฉฐ, ๋งŒ์•ฝ ์ง€ํ‚ค์ง€ ์•Š์•˜๋‹ค๋ฉด ์ด ํด๋ž˜์Šค์˜ ์ˆœ์„œ๋Š” equals ๋ฉ”์„œ๋“œ์™€ ์ผ๊ด€๋˜์ง€ ์•Š๋Š” ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•ด์•ผํ•œ๋‹ค.)

compareTo ๊ทœ์•ฝ์„ ์ง€ํ‚ค์ง€ ๋ชปํ•˜๋ฉด ๋น„๊ต๋ฅผ ํ™œ์šฉํ•˜๋Š” ํด๋ž˜์Šค(TreeSet, TreeMap, Collections, Arrays)๋ฅผ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค.

4๋ฒˆ ๊ทœ์•ฝ์„ ์ง€ํ‚ค์ง€ ์•Š์€ ์ฆ‰, compareTo์™€ equals์˜ ๊ฒฐ๊ณผ๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋Š” ์ด ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ์ •๋ ฌ๋œ ์ปฌ๋ ‰์…˜์—์„œ ์˜๋„์น˜ ์•Š์€ ๋™์ž‘์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

    public static void main(String[] args) {

        BigDecimal a = new BigDecimal("1.0");
        BigDecimal b = new BigDecimal("1.00");

        Set<BigDecimal> hs = new HashSet<>();
        hs.add(a);
        hs.add(b);

        System.out.println(hs.size()); // 2

        Set<BigDecimal> ts = new TreeSet<>();
        ts.add(a);
        ts.add(b);

        System.out.println(ts.size()); // 1

    }

HashSet์€ equals ๋ฉ”์†Œ๋“œ๋กœ ๋น„๊ต๋ฅผ ํ•˜๊ธฐ๋•Œ๋ฌธ์— HashSet์˜ ์›์†Œ๋Š” 2๊ฐœ์ด๊ณ , TreeSet์€ compareTo ๋ฉ”์„œ๋“œ๋กœ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›์†Œ์˜ ๊ฐœ์ˆ˜๋Š” 1๊ฐœ์ด๋‹ค.

Comparable์€ ํƒ€์ž…์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ œ๋„ค๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค๋กœ, compareTo ๋ฉ”์„œ๋“œ์˜ ์ธ์ˆ˜ ํƒ€์ž…์€ ์ปดํŒŒ์ผํƒ€์ž„์— ์ •ํ•ด์ง„๋‹ค. ์ธ์ˆ˜ ํƒ€์ž… ์ž์ฒด๊ฐ€ ์ž˜๋ชป๋๋‹ค๋ฉด, ์ปดํŒŒ์ผ ์ž์ฒด๊ฐ€ ๋˜์ง€ ์•Š์œผ๋ฉฐ, null์„ ์ธ์ˆ˜๋กœ ๋„ฃ์–ด ํ˜ธ์ถœํ•œ๋‹ค๋ฉด NullPointerExeption์ด ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค. compareTo๋Š” ๊ฐ ํ•„๋“œ๊ฐ€ ๋™์น˜์ธ์ง€ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ˆœ์„œ๋ฅผ ๋น„๊ตํ•œ๋‹ค.

compareTo ๋ฉ”์„œ๋“œ์—์„œ ๊ด€๊ณ„ ์—ฐ์‚ฐ์ž <์™€ > ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ์ถ”์ฒœํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์ž๋ฐ”7๋ถ€ํ„ฐ ๋ฐ•์‹ฑ๋œ ๊ธฐ๋ณธ ํƒ€์ž… ํด๋ž˜์Šค๋“ค์— ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ compare์„ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค.

ํด๋ž˜์Šค์˜ ํ•ต์‹ฌ ํ•„๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ํ•„๋“œ๋ถ€ํ„ฐ ๋น„๊ตํ•ด์•ผํ•œ๋‹ค. ๋น„๊ต ๊ฒฐ๊ณผ๊ฐ€ 0์ด ์•„๋‹ˆ๋ผ๋ฉด(์ˆœ์„œ๊ฐ€ ์ •ํ•ด์ง„๋‹ค๋ฉด) ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋œ๋‹ค.

  public int compareTo(PhoneNumber pn){
        int result = Short.compare(areaCode, pn.areaCode);
        if(result == 0){
            result = Short.compare(prefix, pn.prefix);
            if(result == 0){
                result = Short.compare(lineNum, pn.lineNum);
            }   
        }
        return result;
    }

์ž๋ฐ” 8์—์„œ๋Š” Comparator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

private static final Comparator<PhoneNumber> COMPARATOR = Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode).thenComparingInt(pn -> pn.lineNum).thenComparingInt(pn -> pn.prefix);

    public int compareTo(PhoneNumber pn){
        return COMPARATOR.compare(this, pn);
    }

์ด ๋ฐฉ์‹์€ ๊ฐ„๊ฒฐํ•˜์ง€๋งŒ ,์•ฝ๊ฐ„์˜ ์„ฑ๋Šฅ์ €ํ•˜๊ฐ€ ๋’ค๋”ฐ๋ฅธ๋‹ค. Comparator๋Š” ์ž๋ฐ”์˜ ์ˆซ์ž์šฉ ๊ธฐ๋ณธ ํƒ€์ž…์„ ๋ชจ๋‘ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋น„๊ต์ž ์ฃผ์˜ ์‚ฌํ•ญ

static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return o1.hashCode() - o2.hashCode();
    }
};

๋‹ค์Œ ๋ฐฉ์‹์€ ์ •์ˆ˜ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋‚˜, ๋ถ€๋™์†Œ์ˆ˜์  ๊ณ„์‚ฐ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ ์˜ค๋ฅ˜๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์„ฑ๋Šฅ ๋˜ํ•œ ์›”๋“ฑํžˆ ๋น ๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์•„๋ž˜ ๋‘ ๋ฐฉ์‹์ค‘ ํ•˜๋‚˜๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

  • ์ •์  compare ๋ฉ”์„œ๋“œ ํ™œ์šฉ

static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2){
        return Integer.compare(o1.hashCode(), o2.hashCode());
    }
}
  • ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ ํ™œ์šฉ

static Comparator<Object> hashCodeOrder = Comparator.comparingInt(o->o.hashCode);

์ •๋ฆฌํ•˜์ง€๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ์ˆœ์„œ๋ฅผ ๊ณ ๋ คํ•˜๋Š” ๊ฐ’ ํด๋ž˜์Šค ์ž‘์„ฑ์‹œ Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ์‰ฝ๊ฒŒ ์ •๋ ฌ, ๊ฒ€์ƒ‰, ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š” ์ปฌ๋ ‰์…˜๊ณผ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

  2. compareTo ๋ฉ”์„œ๋“œ์—์„œ ํ•„๋“œ ๊ฐ’ ๋น„๊ต์‹œ <, > ์—ฐ์‚ฐ์ž๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž

  3. ๋ฐ•์‹ฑ๋œ ๊ธฐ๋ณธ ํƒ€์ž… ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ •์  compare ๋ฉ”์„œ๋“œ๋‚˜ Compartor ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

Last updated