Question

J'écris une application qui doit générer des décimales de différentes longueurs et échelles variables pour les chaînes sans décimale pour l'écriture dans un fichier plat en tant qu'entrée vers un autre système. par exemple

 12345  -> Length:10, Scale:2              -> 0001234500
 123.45 -> Length:10, Scale:2              -> 0000012345
 123.45 -> Length:10, Scale:3              -> 0000123450
-123.45 -> Length:10, Scale:3, signed:true -> -000123450
 123.45 -> Length:10, Scale:3, signed:true -> +000123450

La fonction que j'ai écrite pour gérer ceci est ci-dessous et sera appelée des centaines de milliers de fois. Je veux donc m'assurer qu'il n'y a pas de meilleure façon, plus efficace, de faire cela. J'ai cherché des moyens de faire en sorte que DecimalFormat en fasse plus pour moi, mais je ne le vois pas gérer mon besoin de formater avec des décimales, mais sans séparateur décimal.

protected String getFormattedDecimal( String value, int len, int scale, Boolean signed ) throws Exception{
    StringBuffer retVal = new StringBuffer();

    //Need a BigDecimal to facilitiate correct formatting
    BigDecimal bd = new BigDecimal( value );

    //set the scale to ensure that the correct number of zeroes 
    //at the end in case of rounding
    bd = bd.setScale( scale );

    //taking it that if its supposed to have negative it'll be part of the num
    if ( ( bd.compareTo( BigDecimal.ZERO ) >= 0 ) && signed ){
        retVal.append( "+" );
    }           

    StringBuffer sbFormat = new StringBuffer();
    for (int i = 0; i < len; i++)
    {
        sbFormat.append('0');
    }

    DecimalFormat df = new DecimalFormat( sbFormat.toString() );

    retVal.append( df.format( bd.unscaledValue() ) );

    return retVal.toString();
}
Était-ce utile?

La solution

Ma mise en œuvre améliorée en termes de performances est ci-dessous. Elle est environ 4,5 fois plus rapide que la solution basée sur DecimalFormatter: en cours d’exécution sur ma machine, en utilisant Eclipse avec un bon harnais de test préparé à la maison, les résultats sont les suivants:

  • Il fallait 5421 ms pour passer 600 000 appels (en moyenne 0,009035 ms par appel).
  • Il a fallu 1219 ms à la nouvelle manière pour passer 600 000 appels (en moyenne 0,002032 ms par appel).

Quelques notes:

  • Ma solution utilise un bloc de zéros de taille fixe pour le remplissage. Si vous prévoyez avoir besoin de plus de rembourrage de chaque côté que la trentaine que j'ai utilisée, vous devrez augmenter la taille ... clairement, vous pouvez l'augmenter dynamiquement si nécessaire.
  • Vos commentaires ci-dessus ne correspondaient pas tout à fait au code. En particulier, si un caractère de signe a été renvoyé, la longueur renvoyée est supérieure à celle demandée (vos commentaires indiquent le contraire). J'ai choisi de croire le code plutôt que les commentaires.
  • J'ai rendu ma méthode statique, car elle ne nécessite aucun état d'instance. C'est une affaire de goût personnel - ymmv.

Aussi: pour imiter le comportement de l'original (mais pas dans les commentaires), ceci:

  • S'il y a plus de chiffres fractionnaires dans la valeur entrante que dans l'ajustement d'échelle, lève une exception ArithmeticException S'il y a plus de chiffres entiers dans la valeur entrante que dans la valeur len-scale, la chaîne renvoyée est plus longue que len. Si signé est vrai, la chaîne retournée sera plus longue que len

    • Cependant : si len est négatif, l'original renvoie une chaîne délimitée par des virgules. Ceci jette une exception IllegalARgumentException
    package com.pragmaticsoftwaredevelopment.stackoverflow;
    ...
       final static String formatterZeroes="00000000000000000000000000000000000000000000000000000000000";
       protected static String getFormattedDecimal ( String value, int len, int scale, Boolean signed ) throws IllegalArgumentException {
           if (value.length() == 0) throw new IllegalArgumentException ("Cannot format a zero-length value");
           if (len <= 0) throw new IllegalArgumentException ("Illegal length (" + len + ")");
           StringBuffer retVal = new StringBuffer();
           String sign=null;
           int numStartIdx; 
           if ("+-".indexOf(value.charAt(0)) < 0) {
              numStartIdx=0;
           } else {
              numStartIdx=1;
              if (value.charAt(0) == '-')
                 sign = "-";
           }
           if (signed && (value.charAt(0) != '-'))
              sign = "+";
           if (sign==null)
              sign="";
           retVal.append(sign);
    
    
           int dotIdx = value.indexOf('.');
           int requestedWholePartLength = (len-scale);
    
           if (dotIdx < 0) { 
              int wholePartPadLength = (requestedWholePartLength - ((value.length()-numStartIdx)));
              if (wholePartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, wholePartPadLength));
              retVal.append (value.substring(numStartIdx));
              if (scale > 0)
                 retVal.append(formatterZeroes.substring(0, scale));
           }
           else {
              int wholePartPadLength = (requestedWholePartLength - (dotIdx - numStartIdx));
              if (wholePartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, wholePartPadLength));
              retVal.append (value.substring(numStartIdx, dotIdx));
              retVal.append (value.substring (dotIdx+1));
              int fractionalPartPadLength = (scale - (value.length() - 1 - dotIdx));
              if (fractionalPartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, fractionalPartPadLength));
    
    
           }
    
           return retVal.toString();
       }
    
        
  • Autres conseils

    Si vous obtenez votre entrée sous forme de chaîne pour commencer, pourquoi devez-vous la convertir en BigDecimal et inversement?

    Il semble qu’il serait beaucoup plus rapide de trouver la position du point décimal, de le comparer à la longueur / l’échelle et d’enfiler la chaîne en conséquence.

    Je suis d’accord avec ChssPly76 en ce qui concerne la manipulation manuelle des chaînes.

    Cependant, si vous voulez descendre la route BigDecimal / DecimalFormat , vous pouvez envisager de partager vos DecimalFormat au lieu de créer un nouveau à chaque itération. Notez que ces classes ne sont pas thread-safe, donc si vous utilisez plusieurs threads pour effectuer votre traitement, vous souhaiterez utiliser quelque chose comme stockage ThreadLocal pour maintenir un formateur par Thread .

    btw, avez-vous testé cela et trouvé que les performances n'étaient pas acceptables ou êtes-vous simplement à la recherche de la solution la plus efficace possible? Notez ce que Donald Knuth a dit au sujet de l'optimisation initiale .

    Licencié sous: CC-BY-SA avec attribution
    Non affilié à StackOverflow
    scroll top