لماذا لا جافا.لانغ.عدد تنفذ مقارنة ؟ [مكررة]

StackOverflow https://stackoverflow.com/questions/480632

  •  20-08-2019
  •  | 
  •  

سؤال

هذا السؤال سبق الجواب هنا:

لا أحد يعرف لماذا java.lang.Number لا تنفذ Comparable?وهذا يعني أنه لا يمكنك فرز Numbers مع 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().

لذا أعتقد الشمس جعلت (معقولة) قرار Numbers فقط 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) الطريقة ؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top