有谁知道为什么java.lang.Number
没有实现Comparable
?这意味着你无法用Number
对Collections.sort
进行排序,这对我来说有点奇怪。
发布讨论更新:
感谢所有有用的回复。我最终做了some more research about this topic。
为什么java.lang.Number没有实现Comparable的最简单的解释源于可变性问题。
要进行一些审核,java.lang.Number
是AtomicInteger
,AtomicLong
,BigDecimal
,BigInteger
,Byte
的抽象超类型,Double
,Float
,Integer
,Long
和Short
。在该列表中,AtomicInteger
和AtomicLong
未实现Comparable
。
四处搜索,我发现在可变类型上实现Comparable
并不是一个好习惯,因为对象可能会在比较期间或之后发生变化,导致比较结果无效。 AtomicLong
和AtomicInteger
都是可变的。 API设计者已经考虑到没有Number
实现Comparable
,因为它会限制未来子类型的实现。实际上,AtomicLong
和AtomicInteger
在最初实施java.lang.Number
之后很久就在Java 1.5中添加了。
除了可变性之外,这里也可能有其他考虑因素。 compareTo
中的Number
实施必须将所有数值提升为BigDecimal
,因为它能够容纳所有Number
子类型。这个促销在数学和表现方面的含义对我来说有点不清楚,但我的直觉发现解决方案很难。
答案 0 :(得分:63)
值得一提的是以下表达式:
new Long(10).equals(new Integer(10))
总是false
,这往往会让某些人在某个时刻绊倒。因此,您不仅无法比较任意Number
,而且甚至无法确定它们是否相等。
此外,对于真实的原始类型(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做出了Number
仅Comparable
对他们自己的实例的(合理的)决定。
答案 1 :(得分:42)
有关答案,请参阅Java bugparade bug 4414323。您还可以从comp.lang.java.programmer
中找到讨论引用Sun对2001年错误报告的回应:
所有“数字”都不具有可比性; 可比较假设总排序 数字是可能的。这不是偶数 浮点数是真的;为NaN (不是数字)既不低于, 大于,等于任何 浮点值,甚至本身。 {Float,Double} .compare总计 订购不同于订购 浮点数“&lt;”和“=” 运营商。此外,目前 实现,Number的子类 仅与其他实例相当 同一类。还有其他 案例,如复数,其中没有 存在标准总排序, 虽然可以定义一个。在 简而言之,无论是否为子类 数字可比较应保留为 对该子类的决定。
答案 2 :(得分:4)
为了实现数字的可比较,你必须为每个子类对编写代码。它更容易,只是允许子类实现可比较的。
答案 3 :(得分:3)
很可能因为比较数字效率相当低 - 每个数字可以容纳这种比较的唯一表示形式是BigDecimal。
相反,Number的非原子子类实现了Comparable本身。
原子可变,因此不能实现原子比较。
答案 4 :(得分: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);
答案 5 :(得分:3)
要尝试解决原始问题(对数字列表进行排序),可以选择声明扩展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));
}
答案 6 :(得分:1)
对于不同类型的数字没有标准比较。 但是,您可以编写自己的比较器并使用它来创建TreeMap&lt; Number,Object&gt;,TreeSet&lt; Number&gt;或Collections.sort(List&lt; Number&gt;,Comparator)或Arrays.sort(Number [],Comparator);
答案 7 :(得分:1)
编写自己的比较器
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() + ", ");
}
}
}
答案 8 :(得分: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。
答案 9 :(得分:0)
我的猜测是,通过不实现Comparable,它可以更灵活地实现类来实现它。所有常用数字(Integer,Long,Double等)都实现了Comparable。只要元素本身实现Comparable,您仍然可以调用Collections.sort。
答案 10 :(得分:0)
查看类层次结构。像Long,Integer等包装类实现Comparable,即Integer与整数相当,long可以与long相比,但你不能混用它们。至少有这种泛型范例。我猜这回答你的问题'为什么'。
答案 11 :(得分:0)
byte
(原始)是int
(原始)。基元一次只有一个值
语言设计规则允许这样做。
int i = 255
// down cast primitive
(byte) i == -1
Byte
不是Integer
。 Byte
是Number
,Integer
是Number
。 Number
个对象可以同时拥有多个值。
Integer iObject = new Integer(255);
System.out.println(iObject.intValue()); // 255
System.out.println(iObject.byteValue()); // -1
如果Byte
为Integer
且Integer
为Number
,您将在compareTo(Number number1, Number number2)
方法中使用哪一个值?