item 14 junghyunlyoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

Comparable ์ธํ„ฐํŽ˜์ด์Šค

public interface Comparable<T> {
    /**
     * Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     *
     * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
     * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
     * <tt>y.compareTo(x)</tt> throws an exception.)
     *
     * <p>The implementor must also ensure that the relation is transitive:
     * <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
     * <tt>x.compareTo(z)&gt;0</tt>.
     *
     * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
     * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
     * all <tt>z</tt>.
     *
     * <p>It is strongly recommended, but <i>not</i> strictly required that
     * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any
     * class that implements the <tt>Comparable</tt> interface and violates
     * this condition should clearly indicate this fact.  The recommended
     * language is "Note: this class has a natural ordering that is
     * inconsistent with equals."
     *
     * <p>In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.
     *
     * @param   o the object to be compared.
     * @return  a negative integer, zero, or a positive integer as this object
     *          is less than, equal to, or greater than the specified object.
     *
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     *         from being compared to this object.
     */
    public int compareTo(T o);
}

๊ฐ์ฒด๊ฐ„์˜ ๋น„๊ต๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋“ค์€ ์„œ๋กœ๊ฐ„์˜ ๋น„๊ต ์—ฐ์‚ฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

(์•„๋ž˜์— ๋“ฑ์žฅํ•˜๋Š” ๊ตฌํ˜„ ๊ทœ์•ฝ์„ ์ง€ํ‚จ๋‹ค๋Š” ์ „์ œ ํ•˜์—)

๋˜ํ•œ ์ด๋Ÿฌํ•œ ๊ฐ์ฒด๋“ค์€ ์ž์—ฐ์ ์ธ ์ˆœ์„œ(natural order)๊ฐ€ ์žˆ์Œ์„ ๋œปํ•˜๊ฒŒ ๋œ๋‹ค.

์ด๋Ÿฌํ•œ ํŠน์ง• ๋•๋ถ„์— ๋น„๊ต ์—ฐ์‚ฐ์ด ํฌํ•จ๋œ ์—ฌ๋Ÿฌ ์ž๋ฐ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์€

์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋“ค์—๊ฒŒ Arrays.sort์ด๋‚˜ Collections.addAll ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

์‚ฌ์‹ค์ƒ ์ž๋ฐ” ํ”Œ๋žซํผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ชจ๋“  ๊ฐ’ ํด๋ž˜์Šค์™€ ์—ด๊ฑฐ ํƒ€์ž…(item 34)์ด Comparable์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

์•ŒํŒŒ๋ฒณ, ์ˆซ์ž, ์—ฐ๋Œ€ ๊ฐ™์ด ์ˆœ์„œ๊ฐ€ ๋ช…ํ™•ํ•œ ๊ฐ’ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•œ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ž.

compareTo

compareTo ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ์ง€์ผœ์•ผ ํ•˜๋Š” ๊ทœ์•ฝ์ด ์žˆ๋‹ค.

1. ๋งค๊ฐœ๋ณ€์ˆ˜ ์ธ์Šคํ„ด์Šค์™€ ์ž์‹ ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋น„๊ตํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

์ž์‹ ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ธ์Šคํ„ด์Šค๋ณด๋‹ค ์ž‘์œผ๋ฉด ์Œ์˜ ์ •์ˆ˜๋ฅผ, ๊ฐ™์œผ๋ฉด 0์„, ํฌ๋ฉด ์–‘์˜ ์ •์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ž์‹ ์˜ ์ธ์Šคํ„ด์Šค์™€ ๋น„๊ตํ•  ์ˆ˜ ์—†๋Š” ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ฃผ์–ด์ง€๋ฉด ClassCastException์„ ๋˜์ง„๋‹ค.
2. ๋ชจ๋“  x,y์— ๋Œ€ํ•ด sgn(x.compareTo(y)) == -sgn(y.compareTo(x))์—ฌ์•ผ ํ•œ๋‹ค.

* sgn => ํ‘œํ˜„์‹์˜ ๊ฐ’์ด ์Œ์ˆ˜, 0, ์–‘์ˆ˜์ผ ๋•Œ ๊ฐ๊ฐ -1, 0, 1์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
3. x.compareTo(y) > 0 && y.compareTo(z) > 0 ์ด๋ฉด x.compareTo(z) > 0์ด๋‹ค. ์ฆ‰, ์ถ”์ด์„ฑ์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.
4. x.compareTo(y) == 0 ์ด๋ฉด ๋ชจ๋“  z์— ๋Œ€ํ•ด sgn(x.compareTo(z)) == sgn(y.compareTo(z)) ์ด๋‹ค.
5. (x.compareTo(y) == 0) == (x.equals(y))์—ฌ์•ผ ํ•œ๋‹ค. ์ด ๊ทœ์•ฝ์„ ์ง€ํ‚ค์ง€ ์•Š๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๋Š” ๊ทธ ์‚ฌ์‹ค์„ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค.

์˜ˆ) "์ฃผ์˜: ์ด ํด๋ž˜์Šค์˜ ์ˆœ์„œ๋Š” equals ๋ฉ”์„œ๋“œ์™€ ์ผ๊ด€๋˜์ง€ ์•Š๋‹ค." 

๋ชจ๋“  ๊ฐ์ฒด์— ๋Œ€ํ•ด ์ „์—ญ ๋™์น˜๊ด€๊ณ„๋ฅผ ๋ถ€์—ฌํ•˜๋Š” equals ๋ฉ”์„œ๋“œ์™€ ๋‹ฌ๋ฆฌ,

(๋ชจ๋“  ์ผ€์ด์Šค์— ๋Œ€ํ•ด true or false๋ฅผ return ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋กœ ํ•ด์„๋œ๋‹ค.)

compareTo๋Š” ํƒ€์ž…์ด ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

ํƒ€์ž…์ด ๋‹ค๋ฅธ ๊ฐ์ฒด๊ฐ€ ์ฃผ์–ด์ง€๋ฉด ๊ฐ„๋‹จํžˆ ClassCastException์„ ๋˜์ ธ๋„ ๋œ๋‹ค.

๋ฌผ๋ก  ๋‹ค๋ฅธ ํƒ€์ž… ์‚ฌ์ด์˜ ๋น„๊ต๋„ ํ—ˆ์šฉํ•˜๋Š”๋ฐ ๋ณดํ†ต์€ ๋น„๊ตํ•  ๊ฐ์ฒด๋“ค์ด ๊ตฌํ˜„ํ•œ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งค๊ฐœ๋กœ ์ด๋ค„์ง„๋‹ค.


hashCode ๊ทœ์•ฝ์„ ์ง€ํ‚ค์ง€ ๋ชปํ•˜๋ฉด ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค์™€ ์–ด์šธ๋ฆฌ์ง€ ๋ชปํ•˜๊ฒŒ ๋œ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ compareTo ๊ทœ์•ฝ์„ ์ง€ํ‚ค์ง€ ๋ชปํ•˜๋ฉด ๋น„๊ต๋ฅผ ํ™œ์šฉํ•˜๋Š” ํด๋ž˜์Šค์™€ ์–ด์šธ๋ฆฌ์ง€ ๋ชปํ•œ๋‹ค.

๋น„๊ต๋ฅผ ํ™œ์šฉํ•˜๋Š” ํด๋ž˜์Šค์˜ ์˜ˆ๋กœ๋Š” ์ •๋ ฌ๋œ ์ปฌ๋ ‰์…˜์ธ TreeSet๊ณผ TreeMap์ด ์žˆ๊ณ  ๊ฒ€์ƒ‰๊ณผ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ™œ์šฉํ•˜๋Š”

์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค์ธ Collections์™€ Arrays๊ฐ€ ์žˆ๋‹ค.


์œ„์˜ compareTo ๋งˆ์ง€๋ง‰ ๊ทœ์•ฝ์€ ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ ๊ผญ ์ง€ํ‚ค๊ธธ ๊ถŒํ•œ๋‹ค.

๋งˆ์ง€๋ง‰ ๊ทœ์•ฝ์€ ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด compareTo ๋ฉ”์„œ๋“œ๋กœ ์ˆ˜ํ–‰ํ•œ ๋™์น˜์„ฑ ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๊ฐ€ equals์˜ ๊ฒฐ๊ณผ์™€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋ฅผ ์ž˜ ์ง€ํ‚ค๋ฉด compareTo๋กœ ์ค„์ง€์€ ์ˆœ์„œ์™€ equals์˜ ๊ฒฐ๊ณผ๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ๋œ๋‹ค.

compareTo์˜ ์ˆœ์„œ์™€ equals์˜ ๊ฒฐ๊ณผ๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š์€ ํด๋ž˜์Šค๋„ ๋™์ž‘์€ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์ด ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ์ •๋ ฌ๋œ ์ปฌ๋ ‰์…˜์— ๋„ฃ์œผ๋ฉด, ํ•ด๋‹น ์ปฌ๋ ‰์…˜์ด ๊ตฌํ˜„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค(Collection, Set, Map)์— ์ •์˜๋œ ๋™์ž‘๊ณผ ์—‡๋ฐ•์ž๋ฅผ ๋‚ผ ๊ฒƒ์ด๋‹ค.

์ด ์ธํ„ฐํŽ˜์ด์Šค๋“ค์€ equals ๋ฉ”์„œ๋“œ์˜ ๊ทœ์•ฝ์„ ๋”ฐ๋ฅธ๋‹ค๊ณ  ๋˜์–ด ์žˆ์ง€๋งŒ ๋†€๋ž๊ฒŒ๋„ ์ •๋ ฌ๋œ ์ปฌ๋ ‰์…˜๋“ค์€ ๋™์น˜์„ฑ์„ ๋น„๊ตํ•  ๋•Œ equals ๋Œ€์‹  compareTo๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

compareTo์™€ equals๊ฐ€ ์ผ๊ด€๋˜์ง€ ์•Š๋Š” BigDecimal ํด๋ž˜์Šค๋ฅผ ์˜ˆ๋กœ ์ƒ๊ฐํ•ด๋ณด์ž.

๋นˆ HashSet ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ๋‹ค์Œ new BigDecimal("1.0")๊ณผ new BigDecimal("1.00")์„ ์ฐจ๋ก€๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค.

์ด ๋‘ BigDecimal์€ equals ๋ฉ”์„œ๋“œ๋กœ ๋น„๊ตํ•˜๋ฉด ์„œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— HashSet์€ ์›์†Œ๋ฅผ 2๊ฐœ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ HashSet ๋Œ€์‹  TreeSet์„ ์‚ฌ์šฉํ•˜๋ฉด ์›์†Œ๋ฅผ ํ•˜๋‚˜๋งŒ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

compareTo ๋ฉ”์„œ๋“œ๋กœ ๋น„๊ตํ•˜๋ฉด ๋‘ BigDecimal ์ธ์Šคํ„ด์Šค๊ฐ€ ๋˜‘๊ฐ™๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflated().equals(xDec.inflated());
}
public int compareTo(BigDecimal val) {
    // Quick path for equal scale and non-inflated case.
    if (scale == val.scale) {
        long xs = intCompact;
        long ys = val.intCompact;
        if (xs != INFLATED && ys != INFLATED)
            return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
    }
    int xsign = this.signum();
    int ysign = val.signum();
    if (xsign != ysign)
        return (xsign > ysign) ? 1 : -1;
    if (xsign == 0)
        return 0;
    int cmp = compareMagnitude(val);
    return (xsign > 0) ? cmp : -cmp;
}

Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ์˜ ์ฃผ์˜์‚ฌํ•ญ

๊ธฐ์กด ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•œ ๊ตฌ์ฒด ํด๋ž˜์Šค์—์„œ ์ƒˆ๋กœ์šด ๊ฐ’ ์ปดํฌ๋„ŒํŠธ(ํ•„๋“œ)๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค๋ฉด

compareTo ๊ทœ์•ฝ์„ ์ง€ํ‚ฌ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. (item 10)

(๊ฐ์ฒด ์ง€ํ–ฅ์  ์ถ”์ƒํ™”์˜ ์ด์ ์„ ํฌ๊ธฐํ•œ๋‹ค๋ฉด ๊ฐ€๋Šฅํ• ์ˆ˜๋„ ์žˆ๋‹ค.)

์ด ์ฃผ์˜์‚ฌํ•ญ์€ equals์—๋„ ์ ์šฉ๋œ๋‹ค. ์šฐํšŒ๋ฒ•์€ ์—ญ์‹œ equals์—์„œ์˜ ์šฐํšŒ๋ฒ•๊ณผ ๊ฐ™๋‹ค.

Comparable์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•ด ๊ฐ’ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํ™•์žฅํ•˜๋Š” ๋Œ€์‹  ๋…๋ฆฝ๋œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ์ด ํด๋ž˜์Šค์— ์›๋ž˜ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ•„๋“œ๋ฅผ ๋‘์ž. 

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‚ด๋ถ€ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” '๋ทฐ' ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์ž. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฐ”๊นฅ ํด๋ž˜์Šค์— ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” compareTo ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ๋Š” ํ•„์š”์— ๋”ฐ๋ผ ๋ฐ”๊นฅ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ•„๋“œ ์•ˆ์— ๋‹ด๊ธด ์›๋ž˜ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋‹ค๋ฃฐ ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
Compareble์€ ํƒ€์ž…์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ œ๋„ค๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฏ€๋กœ compareTo ๋ฉ”์„œ๋“œ์˜ ์ธ์ˆ˜ ํƒ€์ž…์€ ์ปดํŒŒ์ผํƒ€์ž„์— ์ •ํ•ด์ง„๋‹ค. 

์ž…๋ ฅ ์ธ์ˆ˜์˜ ํƒ€์ž…์„ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ํ˜•๋ณ€ํ™˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๋‹ค.

์ธ์ˆ˜์˜ ํƒ€์ž…์ด ์ž˜๋ชป๋๋‹ค๋ฉด ์ปดํŒŒ์ผ ์ž์ฒด๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค. 

๋˜ํ•œ null์„ ์ธ์ˆ˜๋กœ ๋„ฃ์–ด ํ˜ธ์ถœํ•˜๋ฉด NullPointerException์„ ๋˜์ ธ์•ผ ํ•œ๋‹ค. 

๋ฌผ๋ก  ์‹ค์ œ๋กœ๋„ ์ธ์ˆ˜(์ด ๊ฒฝ์šฐ null)์˜ ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•˜๋ ค๋Š” ์ˆœ๊ฐ„ ์ด ์˜ˆ์™ธ๊ฐ€ ๋˜์ ธ์งˆ ๊ฒƒ์ด๋‹ค.

Comparable์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„๊ตํ•  ๋•Œ :: Comparator ๋น„๊ต์ž

(์œ„ ๊ทœ์•ฝ๋Œ€๋กœ ๊ตฌํ˜„๋œ) compareTo๋Š” ๊ฐ ํ•„๋“œ๊ฐ€ ๋™์น˜์ธ์ง€๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๊ทธ ์ˆœ์„œ๋ฅผ ๋น„๊ตํ•œ๋‹ค.

๊ฐ์ฒด ์ฐธ์กฐ ํ•„๋“œ๋ฅผ ๋น„๊ตํ•˜๋ ค๋ฉด compareTo ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค.

Comparable์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ํ•„๋“œ๋‚˜ ํ‘œ์ค€์ด ์•„๋‹Œ ์ˆœ์„œ๋กœ ๋น„๊ตํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋น„๊ต์ž(Comparator)๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋น„๊ต์ž๋Š” ์ง์ ‘ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ž๋ฐ”๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ์ค‘์— ๊ณจ๋ผ ์“ฐ๋ฉด ๋œ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋Š” item 10 ์—์„œ ๊ตฌํ˜„ํ•œ CaseInsensitiveString์˜ compareTo ๋ฉ”์„œ๋“œ๋กœ, ์ž๋ฐ”๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋น„๊ต์ž๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

package effectivejava.chapter3.item14;

import java.util.*;

public final class CaseInsensitiveString
        implements Comparable<CaseInsensitiveString> {

    public int compareTo(CaseInsensitiveString cis) {
        return String.CASE_INSENSITIVE_ORDER.compare(s, cis.s);
    }

}

CaseInsensitiveString์ด Comparable์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ๋ณด์ž. CaseInsensitiveString์˜ ์ฐธ์กฐ๋Š” CaseInsensitiveString ์ฐธ์กฐ์™€๋งŒ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ์ด๋Š” Comparable์„ ๊ตฌํ˜„ํ•  ๋•Œ ์ผ๋ฐ˜์ ์œผ๋กœ ๋”ฐ๋ฅด๋Š” ํŒจํ„ด์ด๋‹ค.

์ด ์ฑ…์˜ 2ํŒ์—์„œ๋Š” compareTo ๋ฉ”์„œ๋“œ์—์„œ ์ •์ˆ˜ ๊ธฐ๋ณธ ํƒ€์ž… ํ•„๋“œ๋ฅผ ๋น„๊ตํ•  ๋•Œ๋Š” ๊ด€๊ณ„ ์—ฐ์‚ฐ์ž <์™€ >๋ฅผ, ์‹ค์ˆ˜ ๊ธฐ๋ณธ ํƒ€์ž… ํ•„๋“œ๋ฅผ ๋น„๊ตํ•  ๋•Œ๋Š” ์ •์  ๋ฉ”์„œ๋“œ์ธ Double.compare์™€ Float.compare๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๊ถŒํ–ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ java 7 ๋ถ€ํ„ฐ๋Š” ์ƒํ™ฉ์ด ๋ณ€ํ–ˆ๋‹ค.

๋ฐ•์‹ฑ๋œ ๊ธฐ๋ณธ ํƒ€์ž… ํด๋ž˜์Šค๋“ค์— ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ์ •์  ๋ฉ”์„œ๋“œ์ธ compare๋ฅผ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค.

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

public final class Integer extends Number
        implements Comparable<Integer>, Constable, ConstantDesc {
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

ํ•„๋“œ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ์ธ ํด๋ž˜์Šค์—์„œ Comparable ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•˜๊ธฐ :: ๋น„๊ต ์ˆœ์„œ

ํด๋ž˜์Šค์— ํ•ต์‹ฌ ํ•„๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ์–ด๋Š ๊ฒƒ์„ ๋จผ์ € ๋น„๊ตํ•˜๋Š๋ƒ๊ฐ€ ์ค‘์š”ํ•ด์ง„๋‹ค.

๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ํ•„๋“œ๋ถ€ํ„ฐ ๋น„๊ตํ•˜์ž. ๊ฐ€์žฅ ํ•ต์‹ฌ์ด ๋˜๋Š” ํ•„๋“œ๊ฐ€ ๋˜‘๊ฐ™๋‹ค๋ฉด ๋˜‘๊ฐ™์ง€ ์•Š์€ ํ•„๋“œ๋ฅผ ์ฐพ์„ ๋•Œ๊นŒ์ง€ ๊ทธ ๋‹ค์Œ์œผ๋กœ ์ค‘์š”ํ•œ ํ•„๋“œ๋ฅผ ๋น„๊ตํ•˜์ž.

์•„๋ž˜๋Š” item 10์˜ PhoneNumber ํด๋ž˜์Šค์˜ compareTo ๋ฉ”์„œ๋“œ์— ์œ„ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•œ ์ฝ”๋“œ๋‹ค.

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;
}

java 8์—์„œ๋Š” Comparator ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ์™€ ํ•จ๊ป˜ ์—ฐ์‡„ ๋ฐฉ์‹์œผ๋กœ ๋น„๊ต์ž๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ๋น„๊ต์ž๋“ค์„ Comparable ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์›ํ•˜๋Š” compareTo ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

(ํ•˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ ์„ฑ๋Šฅ์ด ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.)

์•„๋ž˜๋Š” PhoneNumber ํด๋ž˜์Šค์˜ compareTo ๋ฉ”์„œ๋“œ์— ์—ฐ์‡„ ๋ฐฉ์‹์„ ์ ์šฉํ•œ ์ฝ”๋“œ๋‹ค.

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

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

์ด ์ฝ”๋“œ๋Š” ํด๋ž˜์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ 2๊ฐœ๋ฅผ ์ด์šฉํ•ด ๋น„๊ต์ž๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

๊ทธ ์ค‘ ์ฒซ ๋ฒˆ์งธ์ธ comparingInt๋Š” ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ int ํƒ€์ž… ํ‚ค์— ๋งคํ•‘ํ•˜๋Š” ํ‚ค ์ถ”์ถœ ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„, ๊ทธ ํ‚ค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ˆœ์„œ๋ฅผ ์ •ํ•˜๋Š” ๋น„๊ต์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ •์  ๋ฉ”์„œ๋“œ๋‹ค.

์•ž์˜ ์˜ˆ์—์„œ comparingInt๋Š” ๋žŒ๋‹ค๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉฐ, ์ด ๋žŒ๋‹ค๋Š” PhoneNumber์—์„œ ์ถ”์ถœํ•œ ์ง€์—ญ ์ฝ”๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ „ํ™”๋ฒˆํ˜ธ์˜ ์ˆœ์„œ๋ฅผ ์ •ํ•˜๋Š” Comparator๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด ๋žŒ๋‹ค์—์„œ ์ž…๋ ฅ ์ธ์ˆ˜์˜ ํƒ€์ž…(PhoneNumber pn)์„ ๋ช…์‹œํ•œ ์ ์— ์ฃผ๋ชฉํ•˜์ž.

์ž๋ฐ”์˜ ํƒ€์ž… ์ถ”๋ก  ๋Šฅ๋ ฅ์ด ์ด ์ƒํ™ฉ์—์„œ๋„ ํƒ€์ž…์„ ์•Œ์•„๋‚ผ ๋งŒํผ ๊ฐ•๋ ฅํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—

ํ”„๋กœ๊ทธ๋žจ์ด ์ปดํŒŒ์ผ๋˜๋„๋ก ๋„์™€์ค€ ๊ฒƒ์ด๋‹ค.


๋‘ ์ „ํ™”๋ฒˆํ˜ธ์˜ ์ง€์—ญ ์ฝ”๋“œ๊ฐ€ ๊ฐ™์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ๋น„๊ต ๋ฐฉ์‹์„ ๋” ๋‹ค๋“ฌ์–ด์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ๋‘ ๋ฒˆ์งธ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ์ธ thenComparingInt๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค.

thenComparingInt๋Š” Comparator์˜ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋กœ, int ํ‚ค ์ถ”์ถœ์ž ํ•จ์ˆ˜๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ๋‹ค์‹œ ๋น„๊ต์ž๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

(์ด ๋น„๊ต์ž๋Š” ์ฒซ ๋ฒˆ์งธ ๋น„๊ต์ž๋ฅผ ์ ์šฉํ•œ ๋‹ค์Œ ์ƒˆ๋กœ ์ถ”์ถœํ•œ ํ‚ค๋กœ ์ถ”๊ฐ€ ๋น„๊ต๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.)

thenComparingInt๋Š” ์›ํ•˜๋Š” ๋งŒํผ ์—ฐ๋‹ฌ์•„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„ ์ฝ”๋“œ์—์„œ๋Š” 2๊ฐœ๋ฅผ ์—ฐ๋‹ฌ์•„ ํ˜ธ์ถœํ–ˆ์œผ๋ฉฐ ๊ทธ์ค‘ ์ฒซ ๋ฒˆ์งธ์˜ key๋กœ๋Š” prefix๋ฅผ, ๋‘ ๋ฒˆ์งธ์˜ ํ‚ค๋กœ๋Š” lineNum์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

์ด๋ฒˆ์—” thenComparingInt๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ํƒ€์ž…์„ ๋ช…์‹œํ•˜์ง€ ์•Š์•˜๋‹ค.

์™œ๋ƒํ•˜๋ฉด ์ž๋ฐ”์˜ ํƒ€์ž… ์ถ”๋ก  ๋Šฅ๋ ฅ์ด ์ด ์ •๋„๋Š” ์ถ”๋ก ํ•ด๋‚ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

Comparator์˜ ๋ณด์กฐ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ

Comparator๋Š” ์ˆ˜๋งŽ์€ ๋ณด์กฐ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋“ค๋กœ ์ค‘๋ฌด์žฅํ•˜๊ณ  ์žˆ๋‹ค.

long๊ณผ double์šฉ์œผ๋กœ๋Š” thenComparingLong๊ณผ thenComparingDouble์ด ์žˆ๋‹ค.

short์ฒ˜๋Ÿผ ๋” ์ž‘์€ ์ •์ˆ˜ ํƒ€์ž…์—๋Š” ์œ„ PhoneNumber์ฒ˜๋Ÿผ int์šฉ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ float์€ double์šฉ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Comparator ์ด๋ ‡๊ฒŒ ์ž๋ฐ”์˜ ๋ชจ๋“  ์ˆซ์ž์šฉ ๊ธฐ๋ณธ ํƒ€์ž…์— ๋Œ€ํ•œ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.


๋˜ํ•œ ๊ฐ์ฒด ์ฐธ์กฐ์šฉ ๋น„๊ต์ž ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๋„ ์ค€๋น„๋˜์–ด ์žˆ๋‹ค.

์šฐ์„  comparing ์ด๋ผ๋Š” ์ •์  ๋ฉ”์„œ๋“œ 2๊ฐœ๊ฐ€ ๋‹ค์ค‘์ •์˜๋˜์–ด ์žˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” ํ‚ค ์ถ”์ถœ์ž๋ฅผ ๋ฐ›์•„์„œ ๊ทธ ํ‚ค์˜ ์ž์—ฐ์  ์ˆœ์„œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

๋‘ ๋ฒˆ์งธ๋Š” ํ‚ค ์ถ”์ถœ์ž ํ•˜๋‚˜์™€ ์ถ”์ถœ๋œ ํ‚ค๋ฅผ ๋น„๊ตํ•  ๋น„๊ต์ž๊นŒ์ง€ ์ด 2๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค.

public static <T, U> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor,
        Comparator<? super U> keyComparator) {
    Objects.requireNonNull(keyExtractor);
    Objects.requireNonNull(keyComparator);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                          keyExtractor.apply(c2));
}

๋˜ํ•œ thenComparing์ด๋ž€ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๊ฐ€ 3๊ฐœ ๋‹ค์ค‘์ •์˜๋˜์–ด ์žˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” ๋น„๊ต์ž ํ•˜๋‚˜๋งŒ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ๊ทธ ๋น„๊ต์ž๋กœ ๋ถ€์ฐจ ์ˆœ์„œ๋ฅผ ์ •ํ•œ๋‹ค.

default Comparator<T> thenComparing(Comparator<? super T> other) {
    Objects.requireNonNull(other);
    return (Comparator<T> & Serializable) (c1, c2) -> {
        int res = compare(c1, c2);
        return (res != 0) ? res : other.compare(c1, c2);
    };
}

๋‘ ๋ฒˆ์งธ๋Š” ํ‚ค ์ถ”์ถœ์ž๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ๊ทธ ํ‚ค์˜ ์ž์—ฐ์  ์ˆœ์„œ๋กœ ๋ณด์กฐ ์ˆœ์„œ๋ฅผ ์ •ํ•œ๋‹ค.

default <U> Comparator<T> thenComparing(
        Function<? super T, ? extends U> keyExtractor,
        Comparator<? super U> keyComparator)
{
    return thenComparing(comparing(keyExtractor, keyComparator));
}

๋งˆ์ง€๋ง‰ ์„ธ ๋ฒˆ์งธ๋Š” ํ‚ค ์ถ”์ถœ์ž์™€ ์ถ”์ถœ๋œ ํ‚ค๋ฅผ ๋น„๊ตํ•  ๋น„๊ต์ž๊นŒ์ง€ ์ด 2๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค.

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
        Function<? super T, ? extends U> keyExtractor)
{
    return thenComparing(comparing(keyExtractor));
}

์•ˆํ‹ฐ ํŒจํ„ด :: ๊ฐ’์˜ ์ฐจ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋น„๊ต

๊ฐ’์˜ ์ฐจ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฒซ ๋ฒˆ์งธ ๊ฐ’์ด ๋‘ ๋ฒˆ์งธ ๊ฐ’๋ณด๋‹ค ์ž‘์œผ๋ฉด ์Œ์ˆ˜๋ฅผ, ๋‘ ๊ฐ’์ด ๊ฐ™์œผ๋ฉด 0์„, ์ฒซ ๋ฒˆ์งธ ๊ฐ’์ด ํฌ๋ฉด ์–‘์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” compareTo๋‚˜ compare ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๋ฉด ์•ˆ๋œ๋‹ค.

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

์ด ๋ฐฉ์‹์€ ์ •์ˆ˜ ์˜ค๋ฒ„ํ”Œ๋กœ๋ฅผ ์ผ์œผํ‚ค๊ฑฐ๋‚˜ IEEE 754 ๋ถ€๋™์†Œ์ˆ˜์  ๊ณ„์‚ฐ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ ์˜ค๋ฅ˜๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ์ด๋ฒˆ ์•„์ดํ…œ์—์„œ ์„ค๋ช…ํ•œ ๋ฐฉ๋ฒ•๋Œ€๋กœ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋ณด๋‹ค ์›”๋“ฑํžˆ ๋น ๋ฅด์ง€๋„ ์•Š์„ ๊ฒƒ์ด๋‹ค.

์•„๋ž˜์˜ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

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

static Comparator<Object> hashCodeOrder = Comparator.comparingInt(o -> o.hashCode());
โš ๏ธ **GitHub.com Fallback** โš ๏ธ