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

发布于 2024-07-13 04:27:37 字数 1247 浏览 6 评论 0原文

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

讨论后更新:

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

为什么 java.lang.Number 没有实现 Comparable 最简单的解释是源于对可变性的考虑。

稍微回顾一下,java.lang.NumberAtomicIntegerAtomicLongBigDecimal 的抽象超类型>、BigInteger字节双精度浮点整数。 在该列表中,AtomicIntegerAtomicLong 不实现 Comparable

经过深入研究,我发现在可变类型上实现 Comparable 并不是一个好习惯,因为对象可能在比较期间或之后发生变化,从而导致比较结果无用。 AtomicLongAtomicInteger 都是可变的。 API 设计者深思熟虑,不让 Number 实现 Comparable,因为它会限制未来子类型的实现。 事实上,AtomicLongAtomicInteger 是在 java.lang.Number 最初实现之后很久才添加到 Java 1.5 中的。

除了可变性之外,这里可能还有其他考虑因素。 Number 中的 compareTo 实现必须将所有数值提升为 BigDecimal,因为它能够容纳所有 Number > 子类型。 我不太清楚这种提升对数学和表现的影响,但我的直觉发现这个解决方案很糟糕。

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

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.

For a bit of review, java.lang.Number is the abstract super-type of AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long and Short. On that list, AtomicInteger and AtomicLong to do not implement Comparable.

Digging around, I discovered that it is not a good practice to implement Comparable on mutable types because the objects can change during or after comparison rendering the result of the comparison useless. Both AtomicLong and AtomicInteger are mutable. The API designers had the forethought to not have Number implement Comparable because it would have constrained implementation of future subtypes. Indeed, AtomicLong and AtomicInteger were added in Java 1.5 long after java.lang.Number was initially implemented.

Apart from mutability, there are probably other considerations here too. A compareTo implementation in Number would have to promote all numeric values to BigDecimal because it is capable of accommodating all the Number sub-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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

[浮城] 2024-07-20 04:27:37

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

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

总是false,这往往会在某些时候让每个人都陷入困境。 因此,您不仅无法比较任意 Number,甚至无法确定它们是否相等。

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

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
  • 整数
  • 原子整数
  • AtomicLong;
  • 浮动
  • ;
  • 大整数; 和
  • BigDecimal。

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

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

所以我猜 Sun 做出了(合理的)决定:Number 只能与它们自身的实例Comparable

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.

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:

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 Number Comparable. How would you implement it? Using something like doubleValue() wouldn't do it reliably. Remember the Number subtypes are:

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

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

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

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

嘿咻 2024-07-20 04:27:37

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

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

所有“数字”都没有可比性;
可比较假设总排序为
数字是可能的。 这还不是
浮点数为真; 南
(不是数字)不小于,
大于或等于任何
浮点值,甚至它本身。
{Float, Double}.compare 施加总计
排序与排序不同
浮点数“<” 和“=”
运营商。 另外,截至目前
实现了 Number 的子类
仅与其他实例进行比较
同一个班级的。 还有其他的
情况,例如复数,其中没有
存在标准总排序,
尽管可以定义一个。 在
简而言之,无论是否是
具有可比性的数字应保留为
该子类的决定。

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

To quote from the Sun response to the bug report from 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.

剪不断理还乱 2024-07-20 04:27:37

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

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.

街角迷惘 2024-07-20 04:27:37

要尝试解决原始问题(对数字列表进行排序),一种选择是声明扩展 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));
}

To try to solve the original problem (sort a list of numbers), an option is to declare the list of a generic type extending Number and implementing Comparable.

Something like:

<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));
}
鹿童谣 2024-07-20 04:27:37

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

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

原子比较是可变的,因此无法实现原子比较。

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.

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

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

小姐丶请自重 2024-07-20 04:27:37

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

You can use Transmorph to compare numbers using its NumberComparator class.

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);
似狗非友 2024-07-20 04:27:37

编写自己的比较器

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

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() + ", ");
 }
    }
}
牛↙奶布丁 2024-07-20 04:27:37

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

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

听风念你 2024-07-20 04:27:37

为什么这是个坏主意?

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。

why this would have been bad idea?
:

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
}

another option may have been to declare class Number implements Comparable, omit compareTo implementation, and implement it in some classes like Integer while throw UnsupportedException in others like AtomicInteger.

倾听心声的旋律 2024-07-20 04:27:37

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

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.

瀞厅☆埖开 2024-07-20 04:27:37

查看类层次结构。 Long、Integer 等包装类实现了 Comparable,即 Integer 可以与整数进行比较,long 可以与 long 进行比较,但不能混合使用它们。 至少在这个泛型范式中是这样。 我想这回答了你的问题“为什么”。

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'.

春庭雪 2024-07-20 04:27:37

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

int i = 255

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

字节不是整数Byte 是一个NumberInteger 是一个NumberNumber 对象可以同时拥有多个值。

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

如果 ByteInteger 并且 IntegerNumber,您将在 Integer 中使用哪一个值code>compareTo(Number number1, Number number2) 方法?

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

int i = 255

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

A Byte is not an Integer. Byte is a Number and an Integer is a Number. Number objects can have more than one value at the same time.

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

If a Byte is an Integer and an Integer is a Number, Which one value will you use in the compareTo(Number number1, Number number2) method?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文