لماذا لا جافا.لانغ.عدد تنفذ مقارنة ؟ [مكررة]
-
20-08-2019 - |
سؤال
هذا السؤال سبق الجواب هنا:
- مقارنة القيم من اثنين عامة الأرقام 12 إجابات
لا أحد يعرف لماذا java.lang.Number
لا تنفذ Comparable
?وهذا يعني أنه لا يمكنك فرز Number
s مع Collections.sort
الذي يبدو لي غريبا بعض الشيء.
بعد مناقشة تحديث:
شكرا لجميع الردود المفيدة.لقد انتهى به المزيد من البحوث حول هذا الموضوع.
أبسط تفسير لماذا جافا.لانغ.عدد لا ينفذ للمقارنة متجذر في التحولية المخاوف.
قليلا من الاستعراض ، java.lang.Number
هي مجردة سوبر-نوع من AtomicInteger
, AtomicLong
, BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
و Short
.على تلك القائمة ، AtomicInteger
و AtomicLong
أن لا تنفذ Comparable
.
البحث اكتشفت أنها ليست ممارسة جيدة لتنفيذ Comparable
على قابلة للتغيير أنواع لأن الأشياء يمكن أن تتغير أثناء أو بعد المقارنة التقديم نتيجة المقارنة غير مجدية.سواء AtomicLong
و AtomicInteger
هي قابلة للتغيير.API المصممين كان التدبر لا يكون Number
تنفيذ Comparable
لأنه سيكون قيد التنفيذ في المستقبل فرعية.في الواقع ، AtomicLong
و AtomicInteger
أضيفت في جافا 1.5 طويلة بعد java.lang.Number
في البداية تم تنفيذها.
وبصرف النظر عن التحولية ، ربما يكون هناك اعتبارات أخرى هنا أيضا.A compareTo
التنفيذ في Number
يجب أن تشجع كل القيم الرقمية إلى BigDecimal
لأنه قادر على استيعاب كل Number
الأنواع الفرعية.والمغزى من هذا الترويج في مجال الرياضيات و الأداء هو قليلا غير واضحة بالنسبة لي ، ولكن حدسي يرى أن الحل kludgy.
المحلول
الجدير بالذكر أن التعبير التالي:
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()
الطريقة التي لا تتحول إلى سلسلة من إذا instanceof التصريحات ؟ Number
حالات فقط ستة الطرق المتاحة لهم:
byteValue()
;shortValue()
;intValue()
;longValue()
;floatValue()
;وdoubleValue()
.
لذا أعتقد الشمس جعلت (معقولة) قرار Number
s فقط Comparable
إلى حالات من أنفسهم.
نصائح أخرى
عن الجواب ، انظر جافا bugparade علة 4414323.يمكنك أيضا العثور على المناقشة من comp.لانغ.java.مبرمج
أن أقتبس من الشمس رد على تقرير الشوائب من عام 2001:
كل "أرقام" ليست قابلة للمقارنة ؛ مقارنة يفترض مجموع ترتيب الأرقام ممكن.هذا ليس حتى صحيح من أرقام النقطة العائمة;نان (ليس عددا) ولا أقل من ، أكبر من أو يساوي أي قيمة نقطة عائمة ، حتى نفسه.{Float, Double}.قارن فرض المجموع يأمر مختلفة من يأمر من النقطة العائمة "<"و "=" المشغلين.بالإضافة إلى ذلك, حاليا نفذت فرعية من عدد فقط للمقارنة مع غيرها من الحالات من نفس الفئة.وهناك أخرى الحالات ، مثل أرقام معقدة, حيث لا معيار إجمالي الطلب موجود ، على الرغم من أن واحدة يمكن أن تكون محددة.في باختصار, إذا كان أو لم يكن فرعية من عدد مشابه ينبغي أن تترك كما القرار الذي فرعية.
من أجل تنفيذ المماثلة في العدد ، سيكون لديك لكتابة رمز لكل فئة فرعية الزوج.أسهل بدلا من مجرد السماح فرعية لتنفيذ قابلة للمقارنة.
من المحتمل جدا لأنها ستكون غير فعالة بدلا من مقارنة أرقام - فقط تمثيل في كل عدد يمكن أن يصلح أن تسمح مثل هذه المقارنة ستكون BigDecimal.
بدلا من ذلك, غير الذرية فرعية من عدد تنفذ للمقارنة نفسها.
الذرية هي قابلة للتغيير ، لذلك لا يمكن تنفيذ ذرية المقارنة.
يمكنك استخدام 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);
محاولة حل المشكلة الأصلية (فرز قائمة من الأرقام) ، وهو خيار هو أن تعلن قائمة نوع عام توسيع عدد وتنفيذ قابلة للمقارنة.
شيء من هذا القبيل:
<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));
}
لا يوجد stardard مقارنة أعداد من أنواع مختلفة.ومع ذلك يمكنك كتابة الخاصة بك المقارنة واستخدامها لإنشاء TreeMap<Number, Object="">, TreeSet<Number> أو مجموعات.نوع(قائمة<Number>, المقارنة) أو المصفوفات.النوع(عدد[], المقارنة);
الكتابة الخاصة بك المقارنة
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() + ", ");
}
}
}
لماذا هذا كان فكرة سيئة ؟ :
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
}
خيار آخر قد يكون في الدرجة تعلن عدد تنفذ للمقارنة, حذف compareTo التنفيذ ، وتنفيذ ذلك في بعض الفئات مثل صحيح حين رمي UnsupportedException في الآخرين مثل AtomicInteger.
تخميني هو أن عدم تنفيذ للمقارنة ، وإعطاء المزيد من المرونة في تنفيذ دروس لتنفيذ ذلك أم لا.كل مشترك أرقام (Integer, Long, مزدوج, الخ) تنفذ قابلة للمقارنة.لا يزال بإمكانك الاتصال المجموعات.نوع طالما العناصر نفسها تنفيذ للمقارنة.
النظر في التسلسل الهرمي فئة.المجمع فصول طويلة ، صحيح ، وما إلى ذلك ، وتنفيذ مماثلة ، أيعدد صحيح غير قابلة للمقارنة إلى عدد صحيح طويل مشابه منذ فترة طويلة, ولكن لا يمكنك مزجها.على الأقل مع هذه الأدوية النموذج.والتي أعتقد أن يجيب عن سؤالك "لماذا".
byte
(البدائية) هو int
(بدائية).الأوليات إلا قيمة واحدة في كل مرة.
لغة تصميم قواعد يسمح بذلك.
int i = 255
// down cast primitive
(byte) i == -1
A 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)
الطريقة ؟