2016-03-23 4 views
0

Ich muss eine SUM() -Methode schreiben, die Arrays von Number-Objekten (Java) erhalten kann. Es könnten also mehrere Short-Objekte sein, dann ein paar Floats, dann ein BigDecimal.Wie kann ich einen Satz von Java Number-Objekten hinzufügen, um das genaueste Ergebnis zu erhalten?

Wenn alles ein Byte/Short/Integer/Long ist, sollte der zurückgegebene Wert ein Long sein.

Wenn alles ein Byte/Short/Integer/Long/Float/Double ist, sollte der zurückgegebene Wert ein Double sein.

Wenn es ein BigDecimal in der Mischung mit dem oben genannten gibt, dann sollte das Ergebnis ein BigDecimal sein. Und wenn ein BigInteger drin ist, dann bin ich mir nicht sicher, was/wie damit umzugehen ist, da jeder BigInteger unterschiedliche Werte für eine große Zahl haben kann.

Gegenwärtig sammle ich eine laufende Summe von Long (alle Byte/Short/Integer/Long), Double (alle Float/Double) und BigDecimal. Dann am Ende addiere in das was benutzt worden ist (ich habe 2 booleans, die das verfolgen) und gebe das zurück.

Es funktioniert, aber es ist nicht sauber. Gibt es einen leichteren Weg? Eine Möglichkeit, zwei Number-Objekte hinzuzufügen und das entsprechende Number-Objekt zurückzugeben?

+1

Alle von Ihnen genannten Typen können genau als 'BigDecimal' dargestellt werden. Ich würde nur alle Eingaben in 'BigDecimal' konvertieren und hinzufügen. Die Antwort wird genau richtig sein und nicht nur genau. –

+0

Es wird keinen sauberen Weg geben, dies zu tun; Java beabsichtigt nicht, dass Sie verschiedene 'Zahl'-Typen mischen, wie Sie es versuchen. –

+0

@PaulBoddington - Ohne in die Details zu gehen, gibt es Dinge, die wir tun können, wenn wir wissen, dass es ein Long (dh keine Dezimalstellen) oder ein Double ist (muss nicht von BigDecimal konvertiert werden). Außerdem wollen wir schnelle Leistung und meistens ist alles lang oder doppelt. –

Antwort

0

Es gibt keine nette Art und Weise, dies zu tun ist, aber man kann es brechen in vielen kleineren, einfacheren Methoden auf. Mein Ansatz besteht darin, eine Hierarchie von Typen einzurichten. Idealerweise würde die Hinzufügung nur Typen mit niedrigeren Rängen beinhalten. Sobald Sie die passende Stufe für die Addition gefunden haben, können Sie die entsprechende Methode aufrufen. Hier ist mein Code (nicht getestet).

private static final Map<Class<? extends Number>, Integer> RANKS; 

static { 
    Map<Class<? extends Number>, Integer> map = new IdentityHashMap<>(); 
    map.put(Byte.class, 0); 
    map.put(Short.class, 1); 
    map.put(Integer.class, 2); 
    map.put(Long.class, 3); 
    map.put(BigInteger.class, 4); 
    map.put(Float.class, 5); 
    map.put(Double.class, 6); 
    map.put(BigDecimal.class, 7); 
    RANKS = Collections.unmodifiableMap(map); 
} 

private static Number addBytes(Number... numbers) { 
    byte a = 0; 
    for (Number number : numbers) 
     a += number.byteValue(); 
    return a; 
} 

private static Number addShorts(Number... numbers) { 
    short a = 0; 
    for (Number number : numbers) 
     a += number.shortValue(); 
    return a; 
} 

private static Number addInts(Number... numbers) { 
    int a = 0; 
    for (Number number : numbers) 
     a += number.intValue(); 
    return a; 
} 

private static Number addLongs(Number... numbers) { 
    long a = 0; 
    for (Number number : numbers) 
     a += number.longValue(); 
    return a; 
} 

private static Number addBigIntegers(Number... numbers) { 
    BigInteger a = BigInteger.ZERO; 
    for (Number number : numbers) 
     a = a.add(number instanceof BigInteger ? (BigInteger) number : BigInteger.valueOf(number.longValue())); 
    return a; 
} 

private static Number addFloats(Number... numbers) { 
    float a = 0; 
    for (Number number : numbers) 
     a += number.floatValue(); 
    return a; 
} 

private static Number addDoubles(Number... numbers) { 
    double a = 0; 
    for (Number number : numbers) 
     a += number.doubleValue(); 
    return a; 
} 

private static Number addBigDecimals(Number... numbers) { 
    BigDecimal a = BigDecimal.ZERO; 
    for (Number number : numbers) { 
     a = a.add(
       number instanceof BigDecimal ? (BigDecimal) number 
         : number instanceof BigInteger ? new BigDecimal((BigInteger) number) 
         : new BigDecimal(number.doubleValue())); 
    } 
    return a; 
} 

public static Number add(Number... numbers) { 
    if (numbers.length == 0) 
     return 0; 
    int max = -1; 
    for (Number number : numbers) { 
     Integer rank = RANKS.get(number.getClass()); 
     if (rank == null) 
      throw new IllegalArgumentException(); 
     max = Math.max(max, rank); 
    } 
    switch (max) { 
     case 0: return addBytes(numbers); 
     case 1: return addShorts(numbers); 
     case 2: return addInts(numbers); 
     case 3: return addLongs(numbers); 
     case 4: return addBigIntegers(numbers); 
     case 5: return addFloats(numbers); 
     case 6: return addDoubles(numbers); 
     case 7: return addBigDecimals(numbers); 
     default: throw new IllegalStateException(); 
    } 
} 
0

Mit Ihrer Anforderung von Long/Double/BigDecimal Rückgabewert gibt es wahrscheinlich keinen besseren Weg. Ich würde es so lassen oder einfach ein BigDecimal zurückgeben - unterstützt alle Arten und Größen. Gibt es einen Grund, warum du das nicht tust?

Sie könnten jedoch verbessern das Design ein wenig und irgendeine Art von Karte oder Enum erstellen, so dass jeder Typ einen Rückgabetyp Parameter geben und ihnen „Stärke“, BigDecimal außer Kraft gesetzt Lange usw.

0

Ich schrieb zu machen eine Klasse Numbers vor ein paar Monaten, die Ihnen helfen könnte. Werfen Sie einen Blick auf den Source-Code:

https://github.com/pmeisen/gen-misc/blob/master/src/net/meisen/general/genmisc/types/Numbers.java

Im Allgemeinen

  1. Sie die determineCommonType Methode verwenden, um die allgemeinste Art aller Number Instanzen Sie
  2. Sie bestimmen möchten Um alle Werte in Ihrem Array unter Verwendung dieser castToNumber
  3. Einheit in Ihren Array zu konvertieren, fügen Sie die Werte mithilfe einer Implementierung von add für jeden möglichen Typ hinzu (weil der Number Schnittstelle bietet keine solche Methode, Sie müssen es selbst schreiben oder etwas wie longValue verwenden, siehe https://docs.oracle.com/javase/7/docs/api/java/lang/Number.html#longValue()).

Natürlich kann es auch möglich sein, alles zu einem BigDecimal abzubilden, und es dann auf die übliche Art zurückgeworfen die entsprechende Number Implementierung (z.B. Longvalue, wenn Sie eine lange wollen).

Hinweis:

  • Die Implementierung kann auf GitHub unterstützt nicht alle konkreten Number Implementierungen (beispielsweise AtomicInteger), aber ich denke, es kann leicht verbessert werden.
  • Vergessen Sie nicht, dass das Summieren der Werte zu einem Überlauf führen könnte, z., Das Hinzufügen von zwei Integers kann zurückkommen muß ein Long
Verwandte Themen