为什么 java.lang.Number 不实现 Comparable?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/480632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:11:49  来源:igfitidea点击:

Why doesn't java.lang.Number implement Comparable?

javasortingcomparable

提问by Julien Chastang

Does anyone know why java.lang.Numberdoes not implement Comparable? This means that you cannot sort Numbers with Collections.sortwhich seems to me a little strange.

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

Post discussion update:

帖子讨论更新:

Thanks for all the helpful responses. I ended up doing some more research about this topic.

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

The simplest explanation for why java.lang.Number does not implement Comparable is rooted in mutability concerns.

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

For a bit of review, java.lang.Numberis the abstract super-type of AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Longand Short. On that list, AtomicIntegerand AtomicLongto do not implement Comparable.

稍微回顾一下,java.lang.NumberAtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Longand的抽象超类型Short。在该列表中,AtomicInteger并且AtomicLong不执行Comparable.

Digging around, I discovered that it is not a good practice to implement Comparableon mutable types because the objects can change during or after comparison rendering the result of the comparison useless. Both AtomicLongand AtomicIntegerare mutable. The API designers had the forethought to not have Numberimplement Comparablebecause it would have constrained implementation of future subtypes. Indeed, AtomicLongand AtomicIntegerwere added in Java 1.5 long after java.lang.Numberwas initially implemented.

仔细研究,我发现Comparable在可变类型上实现不是一个好习惯,因为对象可能会在比较期间或之后发生变化,从而导致比较结果无用。这两个AtomicLongAtomicInteger是可变的。API 设计者有先见之明,没有Number实现,Comparable因为它会限制未来子类型的实现。确实,AtomicLong并且AtomicIntegerjava.lang.Number最初实现之后很久才被添加到 Java 1.5 中。

Apart from mutability, there are probably other considerations here too. A compareToimplementation in Numberwould have to promote all numeric values to BigDecimalbecause it is capable of accommodating all the Numbersub-types. The implication of that promotion in terms of mathematics and performance is a bit unclear to me, but my intuition finds that solution kludgy.

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

采纳答案by cletus

It's worth mentioning that the following expression:

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

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

is always false, which tends to trip everyone up at some point or another. So not only can you not compare arbitrary Numbers but you can't even determine if they're equal or not.

is always false,这往往会在某些时候绊倒每个人。因此,您不仅不能比较任意Numbers,而且您甚至无法确定它们是否相等。

Also, with the real primitive types (float, double), determining if two values are equal is tricky and has to be done within an acceptable margin of error. Try code like:

此外,对于真正的原始类型 ( 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);

and you'll be left with some small difference.

你会留下一些小的差异。

So back to the issue of making NumberComparable. How would you implement it? Using something like doubleValue()wouldn't do it reliably. Remember the Numbersubtypes are:

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

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

Could you code a reliable compareTo()method that doesn't devolve into a series of if instanceof statements? Numberinstances only have six methods available to them:

你能编写一个可靠的compareTo()方法,它不会演变成一系列 if instanceof 语句吗? Number实例只有六种方法可供它们使用:

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

So I guess Sun made the (reasonable) decision that Numbers were only Comparableto instances of themselves.

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

回答by Pete Kirkham

Very probably because it would be rather inefficient to compare numbers - the only representation into which every Number can fit to allow such comparison would be BigDecimal.

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

Instead, non-atomic subclasses of Number implements Comparable itself.

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

Atomic ones are mutable, so can't implement an atomic comparison.

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

回答by Steve Kuo

My guess is that by not implementing Comparable, it give more flexibility to implementing classes to implement it or not. All the common numbers (Integer, Long, Double, etc) do implement Comparable. You can still call Collections.sort as long as the elements themselves implement Comparable.

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

回答by z -

in order to implement comparable on number, you would have to write code for every subclass pair. Its easier instead to just allow subclasses to implement comparable.

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

回答by airportyh

Looking at the class hierarchy. Wrapper classes like Long, Integer, etc, implement Comparable, i.e. an Integer is comparable to an integer, and a long is comparable to a long, but you can't mix them. At least with this generics paradigm. Which I guess answers your question 'why'.

查看类层次结构。Long、Integer 等包装类实现了 Comparable,即一个 Integer 相当于一个整数,一个 long 相当于一个 long,但你不能混合使用它们。至少有了这个泛型范式。我猜这回答了你的问题“为什么”。

回答by Eddie

For the answer, see Java bugparade bug 4414323. You can also find a discussion from comp.lang.java.programmer

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

To quote from the Sun response to the bug report from 2001:

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

All "numbers" are not comparable; comparable assumes a total ordering of numbers is possible. This is not even true of floating-point numbers; NaN (not a number) is neither less than, greater than, nor equal to any floating-point value, even itself. {Float, Double}.compare impose a total ordering different from the ordering of the floating-point "<" and "=" operators. Additionally, as currently implemented, the subclasses of Number are only comparable to other instances of the same class. There are other cases, like complex numbers, where no standard total ordering exists, although one could be defined. In short, whether or not a subclass of Number is comparable should be left as a decision for that subclass.

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

回答by l_39217_l

byte(primitive) is a int(primitive). Primitives have only one value at a time.
Language design rules allows this.

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

int i = 255

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

A Byteis not an Integer. Byteis a Numberand an Integeris a Number. Numberobjects can have more than one value at the same time.

AByte不是Integer. Byte是 aNumber并且 anIntegerNumberNumber对象可以同时具有多个值。

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

If a Byteis an Integerand an Integeris a Number, Which one value will you use in the compareTo(Number number1, Number number2)method?

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

回答by Peter Lawrey

there is no stardard comparison for Numbers of different types. However you can write your own Comparator and use it to create a TreeMap<Number, Object>, TreeSet<Number> or Collections.sort(List<Number>, Comparator) or Arrays.sort(Number[], Comparator);

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

回答by cchabanois

You can use Transmorphto compare numbers using its NumberComparator class.

您可以使用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);

回答by Hilmar

Write your own Comparator

编写自己的比较器

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() + ", ");
 }
    }
}