HihoBookStudy / EffectiveJava

이펙티브 자바 북스터디입니다.
1 stars 0 forks source link

[Item14] BigDecimal 클래스 #24

Closed zpqmdh closed 2 months ago

zpqmdh commented 3 months ago

책 90쪽에 의하면 BigDecimal 클래스는 compareToequals 가 일관되지 않다고 합니다.

  1. BigDecimal 클래스에서의 compareToequals 메서드가 어떻게 정의되어 있는지 궁금합니다.
  2. HashSetTreeSet 의 비교 방식 차이가 궁금합니다. HashSetequals비교를, TreeSetcompareTo 비교를 한다고 적혀있는데 실제로 그런지 궁금합니다!
ForteEscape commented 3 months ago

Q1.

BigDecimal 클래스에서의 compareToequals가 일관되지 않는데 어떻게 정의되어 있는지 궁금합니다.

A1.


@Override
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());
}

- `BigDecimal` 내부 구현 코드중 일부입니다. 두 메서드 사이에는 책에서 서술한 것 처럼 일관성이 없습니다. 즉 `compareTo`에는 0이 반환되는 데이터가, `equals`를 사용하면 true가 아닐 수 있습니다.
- 가장 주목해야 할 점은 equals의 다음 코드 부분입니다.

```java
        if (!(x instanceof BigDecimal))
            return false;
        BigDecimal xDec = (BigDecimal) x;
        if (x == this)
            return true;
        if (scale != xDec.scale)
            return false;
        ...
    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;
    }
ForteEscape commented 3 months ago

Q2.

HashSetequals 비교를, TreeSetcompareTo 비교를 한다고 적혀있는데 실제로 그런지 궁금합니다!

A2.

  • 그렇습니다. 사실 이는HashSetTreeSet의 성질을 생각해보면 유추해낼 수 있는 것이기도 합니다.
  • TreeSet을 먼저 생각해보면 TreeSet은 "순서를 유지하는 집합"이며 특정 비교 기준을 사용하여 그 기준에 맞도록 원소들을 순서대로 정렬하여 보관합니다.
  • 즉 순서가 존재하기 때문에 equals로 비교를 하기에는 명확한 한계가 존재한다고 할 수 있습니다.
  • Java에서 정의된 TreeMap, TreeSet은 내부적으로 Red-Black-Tree 라고 하는 BinarySearchTree(이하 BST) 자료구조를 기반으로 구현되었습니다. BST에서 데이터가 들어가기 위해서는 먼저 들어가는 데이터가 트리의 원소보다 큰지 작은지에 대한 대소비교를 수행해야 하므로 이 과정에서 compareTo가 필수적으로 활용되어야 합니다.
  • 아래는 TreeMap에서의 containsKey 구현 코드입니다. TreeSetTreeMapcontainsKey를 사용하여 contains 연산을 수행하기 때문에 같이 보셔도 무방합니다.(이는 HashSetHashMap도 동일합니다.)
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    final Entry<K,V> getEntry(Object key) {
        // Offload comparator-based version for sake of performance
        if (comparator != null)
            return getEntryUsingComparator(key);
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }