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

Was this helpful?