149

有谁知道为什么 java.lang.Number不实施Comparable?这意味着您不能对Numbers 进行排序,Collections.sort这在我看来有点奇怪。

发布讨论更新:

感谢所有有用的回复。我最终对这个主题做了更多的研究

为什么 java.lang.Number 不实现 Comparable 的最简单解释源于可变性问题。

稍微回顾一下,是, , , , , , , , andjava.lang.Number的抽象超类型。在那个列表上,并且不执行.AtomicIntegerAtomicLongBigDecimalBigIntegerByteDoubleFloatIntegerLongShortAtomicIntegerAtomicLongComparable

挖掘了一下,我发现Comparable在可变类型上实现并不是一个好习惯,因为在比较期间或之后对象可能会发生变化,从而使比较结果变得无用。AtomicLong和都是AtomicInteger可变的。API 设计者有先见之明没有Number实现Comparable,因为它会限制未来子类型的实现。确实,AtomicLong并且在最初实现AtomicInteger之后很久就被添加到 Java 1.5中。java.lang.Number

除了可变性之外,这里可能还有其他考虑因素。一个compareTo实现Number必须将所有数值提升为,BigDecimal因为它能够容纳所有Number子类型。这种提升在数学和性能方面的含义对我来说有点不清楚,但我的直觉发现这个解决方案很笨拙。

4

12 回答 12

75

值得一提的是以下表达式:

new Long(10).equals(new Integer(10))

总是false,这往往会在某个时候或另一个时候绊倒每个人。因此,您不仅不能比较任意Number的 s,而且您甚至无法确定它们是否相等。

此外,对于真正的原始类型 ( float, double),确定两个值是否相等是很棘手的,并且必须在可接受的误差范围内完成。尝试如下代码:

double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
  d2 += 0.1d;
}
System.out.println(d2 - d1);

你会留下一些小的差异。

所以回到制作的问题Number Comparable。你将如何实施它?使用类似的东西doubleValue()不会可靠地做到这一点。请记住,Number子类型是:

  • Byte;
  • Short;
  • Integer;
  • Long;
  • AtomicInteger;
  • AtomicLong;
  • Float;
  • Double;
  • BigInteger; 和
  • BigDecimal.

您能否编写一个compareTo()不会演变为一系列 if instanceof 语句的可靠方法? Number实例只有六个可用的方法:

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue(); 和
  • doubleValue().

所以我猜 Sun 做出了(合理的)决定,NumberComparable针对他们自己的实例。

于 2009-01-26T21:20:59.177 回答
46

有关答案,请参阅 Java bugparade错误 4414323。您还可以从comp.lang.java.programmer找到讨论

引用 Sun 对 2001 年错误报告的回应:

所有的“数字”都没有可比性;可比较假设数字的总排序是可能的。浮点数甚至都不是这样。NaN(不是数字)既不小于、大于也不等于任何浮点值,甚至它本身。{Float, Double}.compare 施加与浮点“<”和“=”运算符的顺序不同的总顺序。此外,按照目前的实现,Number 的子类只能与同一类的其他实例进行比较。还有其他情况,例如复数,不存在标准的总排序,尽管可以定义一个。简而言之,Number 的子类是否具有可比性应该留给该子类来决定。

于 2009-01-26T17:40:37.657 回答
5

为了实现数字上的可比性,您必须为每个子类对编写代码。相反,只允许子类实现可比较更容易。

于 2009-01-26T17:39:25.693 回答
5

为了尝试解决原始问题(对数字列表进行排序),一个选项是声明一个泛型类型的列表扩展 Number 并实现 Comparable。

就像是:

<N extends Number & Comparable<N>> void processNumbers(List<N> numbers) {
    System.out.println("Unsorted: " + numbers);
    Collections.sort(numbers);
    System.out.println("  Sorted: " + numbers);
    // ...
}

void processIntegers() {
    processNumbers(Arrays.asList(7, 2, 5));
}

void processDoubles() {
    processNumbers(Arrays.asList(7.1, 2.4, 5.2));
}
于 2012-10-18T12:36:38.917 回答
3

很可能是因为比较数字会相当低效 - 每个数字可以适合允许这种比较的唯一表示是 BigDecimal。

相反,Number 的非原子子类实现了 Comparable 本身。

原子的是可变的,所以不能实现原子比较。

于 2009-01-26T17:38:05.023 回答
3

您可以使用Transmorph使用其 NumberComparator 类来比较数字。

NumberComparator numberComparator = new NumberComparator();
assertTrue(numberComparator.compare(12, 24) < 0);
assertTrue(numberComparator.compare((byte) 12, (long) 24) < 0);
assertTrue(numberComparator.compare((byte) 12, 24.0) < 0);
assertTrue(numberComparator.compare(25.0, 24.0) > 0);
assertTrue(numberComparator.compare((double) 25.0, (float) 24.0) > 0);
assertTrue(numberComparator.compare(new BigDecimal(25.0), (float) 24.0) > 0);
于 2010-04-13T12:14:14.943 回答
2

编写自己的比较器

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class NumberComparator implements Comparator {
    @SuppressWarnings("unchecked")
    @Override
    public int compare(Number number1, Number number2) {
 if (((Object) number2).getClass().equals(((Object) number1).getClass())) {
     // both numbers are instances of the same type!
     if (number1 instanceof Comparable) {
  // and they implement the Comparable interface
  return ((Comparable) number1).compareTo(number2);
     }
 }
 // for all different Number types, let's check there double values
 if (number1.doubleValue() < number2.doubleValue())
     return -1;
 if (number1.doubleValue() > number2.doubleValue())
     return 1;
 return 0;
    }

    /**
     * DEMO: How to compare apples and oranges.
     */
    public static void main(String[] args) {
 ArrayList listToSort = new ArrayList();
 listToSort.add(new Long(10));
 listToSort.add(new Integer(1));
 listToSort.add(new Short((short) 14));
 listToSort.add(new Byte((byte) 10));
 listToSort.add(new Long(9));
 listToSort.add(new AtomicLong(2));
 listToSort.add(new Double(9.5));
 listToSort.add(new Double(9.0));
 listToSort.add(new Double(8.5));
 listToSort.add(new AtomicInteger(2));
 listToSort.add(new Long(11));
 listToSort.add(new Float(9));
 listToSort.add(new BigDecimal(3));
 listToSort.add(new BigInteger("12"));
 listToSort.add(new Long(8));
 System.out.println("unsorted: " + listToSort);
 Collections.sort(listToSort, new NumberComparator());
 System.out.println("sorted:   " + listToSort);
 System.out.print("Classes:  ");
 for (Number number : listToSort) {
     System.out.print(number.getClass().getSimpleName() + ", ");
 }
    }
}
于 2010-04-27T12:37:13.087 回答
1

不同类型的数字没有标准比较。但是,您可以编写自己的 Comparator 并使用它来创建 TreeMap<Number, Object>, TreeSet<Number> 或 Collections.sort(List<Number>, Comparator) 或 Arrays.sort(Number[], Comparator);

于 2009-01-26T22:04:22.070 回答
1

为什么这是个坏主意?:

abstract class ImmutableNumber extends Number implements Comparable {
    // do NOT implement compareTo method; allowed because class is abstract
}
class Integer extends ImmutableNumber {
    // implement compareTo here
}
class Long extends ImmutableNumber {
    // implement compareTo here
}

另一种选择可能是声明类 Number 实现 Comparable,省略 compareTo 实现,并在 Integer 等某些类中实现它,而在 AtomicInteger 等其他类中抛出 UnsupportedException。

于 2011-12-08T16:07:09.440 回答
0

我的猜测是,通过不实现 Comparable,它为实现或不实现它的类提供了更大的灵活性。所有常见的数字(Integer、Long、Double 等)都实现了 Comparable。只要元素本身实现 Comparable,您仍然可以调用 Collections.sort。

于 2009-01-26T17:38:45.350 回答
0

查看类层次结构。Long、Integer 等包装类实现了 Comparable,即 Integer 与整数可比较,long 与 long 可比较,但不能混合使用。至少对于这种泛型范式。我想这回答了你的问题'为什么'。

于 2009-01-26T17:40:16.113 回答
0

byte(原始)是一个int(原始)。基元一次只有一个值。
语言设计规则允许这样做。

int i = 255

// down cast primitive
(byte) i == -1

AByte不是Integer. Byte是一个Number,一个Integer是一个NumberNumber对象可以同时具有多个值。

Integer iObject = new Integer(255);
System.out.println(iObject.intValue());   // 255
System.out.println(iObject.byteValue());  // -1

如果 aByte是 anInteger并且 anInteger是 a Number,您将在compareTo(Number number1, Number number2)方法中使用哪个值?

于 2009-01-26T18:50:33.427 回答