سؤال

أريد المقارنة مع المتغيرات ، كلاهما من النوع T extends Number. الآن أريد أن أعرف أي من المتغيرين أكبر من الآخر أو المتساوي. لسوء الحظ ، لا أعرف النوع الدقيق بعد ، فأنا أعلم فقط أنه سيكون نوعًا فرعيًا java.lang.Number. كيف أقوم بذلك؟

تعديل: جربت حل بديل آخر TreeSetS ، الذي عمل بالفعل مع الطلب الطبيعي (بالطبع يعمل ، جميع الفئات الفرعية Number ينفذ Comparable باستثناء Atomicinteger و Atomiclong). وهكذا سأفقد القيم المكررة. عند استخدام Listس، Collection.sort() لن أقبل قائمتي بسبب عدم تطابق ملزمة. غير مرض للغاية.

هل كانت مفيدة؟

المحلول

الحل العاملة (ولكن هش) هو شيء من هذا القبيل:

class NumberComparator implements Comparator<Number> {

    public int compare(Number a, Number b){
        return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
    }

}

لا يزال الأمر غير رائع ، لأنه يعتمد على toString إرجاع قيمة قابلة BigDecimal (التي جافا القياسية Number الفصول تفعل ، ولكن التي Number العقد لا يطلب).

تحرير ، بعد سبع سنوات: كما هو موضح في التعليقات ، هناك (على الأقل؟) ثلاث حالات خاصة toString يمكن أن تنتج تحتاج إلى أخذها في الاعتبار:

نصائح أخرى

هذا يجب أن يعمل لجميع الفئات التي تمتد الرقم ، ويمكن مقارنتها لأنفسهم. عن طريق إضافة & قابلة للمقارنة ، تسمح بإزالة جميع عمليات فحص النوع وتوفر اختبارات نوع وقت التشغيل ورمي الخطأ مجانًا بالمقارنة مع إجابة Sarmun.

class NumberComparator<T extends Number & Comparable> implements Comparator<T> {

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}

أحد الحلول التي قد تعمل من أجلك هو العمل ليس مع T extends Number ولكن مع T extends Number & Comparable. هذا النوع يعني: "T لا يمكن ضبطها إلا على الأنواع التي تنفذ على حد سواء الواجهات ".

يتيح لك ذلك كتابة التعليمات البرمجية التي تعمل مع جميع الأرقام المماثلة. مطبوع بشكل ثابت وأنيق.

هذا هو نفس الحل الذي يقترحه بينيوي ، لكنه يعمل مع جميع أنواع الطرق ، ليس فقط مع فئات المقارنة.

public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
    if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}

public void test() {
    compfunc(2, 1); // Works with Integer.
    compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
    compfunc(2, 1.0); // Compilation error! Different types.
    compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}

بعد طلب أ سؤال مماثل ودراسة الإجابات هنا ، توصلت إلى ما يلي. أعتقد أنه أكثر كفاءة وأكثر قوة من الحل الذي قدمه Gustafc:

public int compare(Number x, Number y) {
    if(isSpecial(x) || isSpecial(y))
        return Double.compare(x.doubleValue(), y.doubleValue());
    else
        return toBigDecimal(x).compareTo(toBigDecimal(y));
}

private static boolean isSpecial(Number x) {
    boolean specialDouble = x instanceof Double
            && (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
    boolean specialFloat = x instanceof Float
            && (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
    return specialDouble || specialFloat;
}

private static BigDecimal toBigDecimal(Number number) {
    if(number instanceof BigDecimal)
        return (BigDecimal) number;
    if(number instanceof BigInteger)
        return new BigDecimal((BigInteger) number);
    if(number instanceof Byte || number instanceof Short
            || number instanceof Integer || number instanceof Long)
        return new BigDecimal(number.longValue());
    if(number instanceof Float || number instanceof Double)
        return new BigDecimal(number.doubleValue());

    try {
        return new BigDecimal(number.toString());
    } catch(final NumberFormatException e) {
        throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
    }
}

الرقم البدائي الأكثر "عامًا" هو مزدوج ، لذلك باستخدام ببساطة

a.doubleValue() > b.doubleValue()

يجب أن تكون كافية في معظم الحالات ، ولكن ... هناك مشكلات خفية هنا عند تحويل الأرقام إلى مضاعفة. على سبيل المثال ، ما يلي ممكن مع BigInteger:

    BigInteger a = new BigInteger("9999999999999992");
    BigInteger b = new BigInteger("9999999999999991");
    System.out.println(a.doubleValue() > b.doubleValue());
    System.out.println(a.doubleValue() == b.doubleValue());

النتائج في:

false
true

على الرغم من أنني أتوقع أن تكون هذه الحالة متطرفة للغاية ، فهذا ممكن. ولا - لا توجد طريقة دقيقة 100 ٪ دقيقة. لا تحتوي واجهة الأرقام على طريقة مثل accessvalue () التحويل إلى نوع قادر على تمثيل الرقم بطريقة مثالية دون فقدان أي معلومات.

في الواقع ، يكون من المستحيل أن يكون لديك مثل هذه الأرقام المثالية بشكل عام - على سبيل المثال تمثيل الرقم PI مستحيل باستخدام أي حساب باستخدام مساحة محدودة.

هذا يجب أن يعمل لجميع الفئات التي تمتد الرقم ، ويمكن مقارنتها لأنفسهم.

class NumberComparator<T extends Number> implements Comparator<T> {

    public int compare(T a, T b){
        if (a instanceof Comparable) 
            if (a.getClass().equals(b.getClass()))
                return ((Comparable<T>)a).compareTo(b);        
        throw new UnsupportedOperationException();
    }
}
if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}

ملحوظة: ال instanceof تحقق من عدم الحاجة بالضرورة - يعتمد على مدى رغبتك في مقارنتها بالضبط. يمكنك بالطبع ببساطة استخدام دائما .doubleValue(), ، لأن كل رقم يجب أن يوفر الأساليب المدرجة هنا.

يحرر: كما هو مذكور في التعليقات ، يجب عليك (دائمًا) التحقق من BigDecimal والأصدقاء. لكنها توفر .compareTo() طريقة:

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 

يمكنك ببساطة استخدام Number's doubleValue() طريقة لمقارنتها ؛ ومع ذلك ، قد تجد أن النتائج ليست دقيقة بما يكفي لاحتياجاتك.

مذا عن هذه؟ بالتأكيد ليست لطيفة ، لكنها تتعامل مع جميع الحالات اللازمة المذكورة.

public class SimpleNumberComparator implements Comparator<Number>
    {
        @Override
        public int compare(Number o1, Number o2)
        {
            if(o1 instanceof Short && o2 instanceof Short)
            {
                return ((Short) o1).compareTo((Short) o2);
            }
            else if(o1 instanceof Long && o2 instanceof Long)
            {
                return ((Long) o1).compareTo((Long) o2);
            }
            else if(o1 instanceof Integer && o2 instanceof Integer)
            {
                return ((Integer) o1).compareTo((Integer) o2);
            }
            else if(o1 instanceof Float && o2 instanceof Float)
            {
                return ((Float) o1).compareTo((Float) o2);
            }
            else if(o1 instanceof Double && o2 instanceof Double)
            {
                return ((Double) o1).compareTo((Double) o2);
            }
            else if(o1 instanceof Byte && o2 instanceof Byte)
            {
                return ((Byte) o1).compareTo((Byte) o2);
            }
            else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
            {
                return ((BigInteger) o1).compareTo((BigInteger) o2);
            }
            else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
            {
                return ((BigDecimal) o1).compareTo((BigDecimal) o2);
            }
            else
            {
                throw new RuntimeException("Ooopps!");
            }

        }

    }

لنفترض أن لديك طريقة مثل:

public <T extends Number> T max (T a, T b) {
   ...
   //return maximum of a and b
}

إذا كنت تعلم أنه لا يوجد سوى أعداد صحيحة ، يمكن تمرير الطويلات والزوجي كمعلمات ، فيمكنك تغيير توقيع الطريقة إلى:

public <T extends Number> T max(double a, double b) {
   return (T)Math.max (a, b);
}

سيعمل هذا لبلد بايت ، قصير ، عدد صحيح ، طويل ومزدوج.

إذا كنت تفترض أنه يمكن تمرير BigInteger's أو BigDecimal أو مزيج من العوامات والمضاعفة ، فلا يمكنك إنشاء طريقة شائعة واحدة لمقارنة جميع هذه الأنواع من المعلمات.

إذا كانت مثيلات رقمك أبداً ذرية (أي AtomicInteger) ثم يمكنك أن تفعل شيئًا مثل:

private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {

 Class<? extends Number> n1Class = n1.getClass();
 if (n1Class.isInstance(n2)) {
  Method compareTo = n1Class.getMethod("compareTo", n1Class);
  return (Integer) compareTo.invoke(n1, n2);
 }

 return -23;
}

هذا منذ كل شيء غير ذري NumberS تنفيذ قابلة للمقارنة

تعديل:

هذا مكلف بسبب التفكير: أعرف

تحرير 2:

هذا بالطبع لا يأخذ حالة تريد من خلالها مقارنة العشرية إلى ints أو بعض هذه ...

تحرير 3:

هذا يفترض أنه لا توجد أحفاد مخصصة للرقم لا تنفذ قابلة للمقارنة (شكرًا djclayworth)

System.out.println(new BigDecimal(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1f).toPlainString());
System.out.println(Float.valueOf(0.1f).toString());
System.out.println(Float.valueOf(0.1f).doubleValue());
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top