比较两个通用数字的值
我想与 T extends Number
类型的变量进行比较。现在我想知道两个变量中哪个大于另一个或相等。不幸的是,我还不知道确切的类型,我只知道它将是 java.lang.Number
的子类型。我怎样才能做到这一点?
编辑:我尝试了使用TreeSet
的另一种解决方法,它实际上适用于自然排序(当然它有效,Number
的所有子类都实现可比较
(AtomicInteger 和 AtomicLong 除外)。因此我会丢失重复的值。使用 List
时,由于绑定不匹配,Collection.sort()
将不接受我的列表。非常不满意。
I want to compare to variables, both of type T extends Number
. Now I want to know which of the two variables is greater than the other or equal. Unfortunately I don't know the exact type yet, I only know that it will be a subtype of java.lang.Number
. How can I do that?
EDIT: I tried another workaround using TreeSet
s, which actually worked with natural ordering (of course it works, all subclasses of Number
implement Comparable
except for AtomicInteger and AtomicLong). Thus I'll lose duplicate values. When using List
s, Collection.sort()
will not accept my list due to bound mismatchs. Very unsatisfactory.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
这应该适用于所有扩展 Number 且与自身可比较的类。通过添加 &与 Sarmun 答案相比,您允许删除所有类型检查,并免费提供运行时类型检查和错误抛出。
This should work for all classes that extend Number, and are Comparable to themselves. By adding the & Comparable you allow to remove all the type checks and provides runtime type checks and error throwing for free when compared to Sarmun answer.
一个可行的(但脆弱的)解决方案是这样的:
不过,它仍然不是很好,因为它依赖于
toString
返回一个可由BigDecimal
解析的值(标准 Java < code>Number 类可以,但Number
合约不需要)。编辑,七年后:正如评论中所指出的,(至少?)三种特殊情况
toString
可能会产生您需要考虑的情况:Infinity
,大于除自身以外的一切-Infinity
,小于除自身以外的一切NaN
,这是非常毛茸茸的/无法比较,因为 所有与NaN
的比较都会导致false
,包括检查与自身是否相等。A working (but brittle) solution is something like this:
It's still not great, though, since it counts on
toString
returning a value parsable byBigDecimal
(which the standard JavaNumber
classes do, but which theNumber
contract doesn't demand).Edit, seven years later: As pointed out in the comments, there are (at least?) three special cases
toString
can produce that you need to take into regard:Infinity
, which is greater than everything, except itself to which it is equal-Infinity
, which is less than everything, except itself to which it is equalNaN
, which is extremely hairy/impossible to compare since all comparisons withNaN
result infalse
, including checking equality with itself.在提出类似问题并研究此处的答案后,我得出了以下结论。我认为它比 gustafc 给出的解决方案更高效、更健壮:
After having asked a similar question and studying the answers here, I came up with the following. I think it is more efficient and more robust than the solution given by gustafc:
一种可能适合您的解决方案是不使用
T extends Number
,而是使用T extends Number & 。可比较
。此类型意味着:“T
只能设置为实现两个接口的类型。”这使您可以编写适用于所有可比较数字的代码。静态类型且优雅。
这与 BennyBoy 提出的解决方案相同,但它适用于各种方法,而不仅仅是比较器类。
One solution that might work for you is to work not with
T extends Number
but withT extends Number & Comparable
. This type means: "T
can only be set to types that implements both the interfaces."That allows you to write code that works with all comparable numbers. Statically typed and elegant.
This is the same solution that BennyBoy proposes, but it works with all kinds of methods, not only with comparator classes.
最“通用”的 Java 原始数字是 double,因此
在大多数情况下简单地使用应该足够了,但是......将数字转换为 double 时,这里存在一些微妙的问题。例如,使用 BigInteger: 可能会
出现以下结果:
尽管我预计这是非常极端的情况,但这是可能的。不,不存在 100% 准确的通用方法。 Number 接口没有像精确值()这样的方法转换为某种能够以完美的方式表示数字而不会丢失任何信息的类型。
实际上,一般来说,拥有如此完美的数字是不可能的 - 例如,使用有限空间的任何算术来表示数字 Pi 是不可能的。
The most "generic" Java primitive number is double, so using simply
should be enough in most cases, but... there are subtle issues here when converting numbers to double. For example the following is possible with BigInteger:
results in:
Although I expect this to be very extreme case this is possible. And no - there is no generic 100% accurate way. Number interface have no method like exactValue() converting to some type able to represent number in perfect way without loosing any information.
Actually having such perfect numbers is impossible in general - for example representing number Pi is impossible using any arithmetic using finite space.
这应该适用于所有扩展 Number 且与自身可比较的类。
This should work for all classes that extend Number, and are Comparable to themselves.
注意:
instanceof
检查不一定需要 - 取决于您想要如何准确地比较它们。当然,您可以简单地始终使用.doubleValue()
,因为每个 Number 都应该提供列出的方法 此处。编辑:正如评论中所述,您将(总是)必须检查 BigDecimal 和朋友。但他们提供了一个
.compareTo()
方法:Note: The
instanceof
check isn't necessarily needed - depends on how exactly you want to compare them. You could of course simply always use.doubleValue()
, as every Number should provide the methods listed here.Edit: As stated in the comments, you will (always) have to check for BigDecimal and friends. But they provide a
.compareTo()
method:您可以简单地使用
Number 的 doubleValue()
方法来比较它们;但是您可能会发现结果不够准确,无法满足您的需求。You can simply use
Number's doubleValue()
method to compare them; however you may find the results are not accurate enough for your needs.这个呢?绝对不好,但它处理提到的所有必要情况。
What about this one? Definitely not nice, but it deals with all necessary cases mentioned.
在我的用例中,我正在寻找一个通用的
Comparator
,它可以与自动装箱基元(64位最大精度)一起使用,而不是像BigInteger
和BigDecimal这样的任意精度类型
。这是第一次尝试。目标是能够比较混合类型(例如,
Float
与Long
)。这也应该适用于那些 AtomicXxx 类型(或任何使用不超过 64 位的手动 Number 子类)。在此排序中,顺便说一句,
Double.NaN > Double.POSITVE_INFINITY > {其他一切}
。In my use case, I was looking for a general
Comparator
that works with the autoboxed primitives (64 bit max precision), not arbitrary precision types likeBigInteger
andBigDecimal
. Here's a first shot at it..The objective is to be able to compare mixed types (e.g.
Float
s againstLong
s). This should also work with those AtomicXxx types (or any hand rolled Number subclass that uses no more than 64 bits).In this ordering, btw,
Double.NaN > Double.POSITVE_INFINITY > { everything else }
.假设您有一些方法,例如:
如果您知道只有整数、长整数和双精度数可以作为参数传递,那么您可以将方法签名更改为:
这适用于字节、短整型、整数、长整型和双精度型。
如果您假设可以传递 BigInteger 或 BigDecimal 或浮点数和双精度数的混合,那么您无法创建一种通用方法来比较所有这些类型的参数。
Let's assume that you have some method like:
If you know that there are only integers, longs and doubles can be passed as parameters then you can change method signature to:
This will work for byte, short, integer, long and double.
If you presume that BigInteger's or BigDecimal's or mix of floats and doubles can be passed then you cannot create one common method to compare all these types of parameters.
如果您的 Number 实例从不是原子的(即 AtomicInteger),那么您可以执行以下操作:
这是因为所有非原子
Number
都实现了 Comparable编辑:
由于反思,这是昂贵的:我知道
编辑2:
这当然不考虑您想要将小数与整数或类似的情况进行比较的情况。 ..
编辑3:
这假设没有自定义的 Number 后代不实现 Comparable (感谢@DJClayworth)
If your Number instances are never Atomic (ie AtomicInteger) then you can do something like:
This is since all non-Atomic
Number
s implement ComparableEDIT:
This is costly due to reflection: I know
EDIT 2:
This of course does not take of a case in which you want to compare decimals to ints or some such...
EDIT 3:
This assumes that there are no custom-defined descendants of Number that do not implement Comparable (thanks @DJClayworth)
似乎不可能以其他方式比较 Number 对象或处理数字与泛型,但是,好消息是只有六种基本数字类型,因此实现看似通用的功能可能会很麻烦实现它,但对最终用户来说看起来不错。
这就是我的实现方式:
此类包装整数(字节、短整型、整数、长整型)并将它们全部转换为长整型。我选择在方法中使用基元以避免进行 null 检查,但将数字存储为
Long
以便与float
和进行比较更容易加倍。
作为该类的用户,您会执行以下操作:
我有另一个用于浮点值的类,其中我使用
Double.compare()
进行所有比较:如果您想更通用地工作使用数字,您可以为类(或实际上六个)创建一个工厂方法,它将输入参数包装在正确的包装类中:
您甚至可以创建一个静态比较方法,例如:
不过,您需要实现其中的 36 个。再说一遍,编码时很辛苦,但使用时非常好,如果 Java 人员可以像他们所做的那样执行
List.of(...)
(至少在 Java 17 中) ,也许 36 个方法并不是那么极端...但是,如果您愿意实现 36 个静态方法,您可能想要执行以下操作:
我的用例是我有一个可能想要使用的数字一次,所以对我来说,将它包装在一个只需要实现 6 个compareTo 方法的类中是最有意义的。
It doesn't seem as if it is possible to compare Number objects or handle numbers with generics in other ways, however, the good news is that there are only six primitive number types, so implementing functionality that seems to be generic may be cumbersome when implementing it but can look nice to the final user.
This is how I've implemented this:
This class wraps integers (byte, short, int, long) and converts them all to Long. I've opted to use primitives in the methods to avoid having to do null checks but stored the number as a
Long
in order to make the comparisons tofloat
anddouble
easier.As a user of the class, you'd do something like:
I have another class for floating point values where I do all comparisons using
Double.compare()
instead:If you want to work even more generically with numbers you could create a factory method for the classes (or actually six) that would wrap the input parameter in the correct wrapper class:
You could even create a static compare method like:
Though, you'd need to implement 36 of them. Again, hard work when coding it, but pretty nice when using it, and if the Java-people can do
List.of(...)
like they've done (at least in Java 17), maybe 36 methods aren't such an extreme...However, if you're willing to implement 36 static methods you might want to do something like:
My use case is that I have a number I might want to work with more than once, so for me, wrapping it in a class where I only have to implement 6 compareTo-methods made the most sense.