Pergunta

Eu estou escrevendo um aplicativo que precisa Decimals saída de vários comprimentos, e variando escala para cordas sem o ponto decimal para gravar em um arquivo simples como entrada para outro sistema. por exemplo.

 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

A função de eu ter escrito para lidar com isso é baixo e vai ser chamado de centenas de milhares de vezes, então eu quero ter certeza de que não há uma maneira melhor e mais eficiente de fazer isso. Eu olhei maneiras de obter DecimalFormat fazer mais para mim, mas eu não posso vê-lo lidar com a minha necessidade de formato com casas decimais, mas sem ponto decimal.

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();
}
Foi útil?

Solução

Meu implementação reforçada desempenho está abaixo. É cerca de 4,5 vezes mais rápido que a solução baseada DecimalFormatter: rodando na minha máquina, usando Eclipse com um equipamento de teste caseiro decente, os resultados são:

  • forma Velho levou 5421 ms para fazer 600.000 chamadas (média de 0.009035 ms por chamada)
  • Nova maneira tomou 1219 ms para fazer 600.000 chamadas (média de 0.002032 ms por chamada)

Algumas notas:

  • A minha solução faz uso de um bloco de tamanho fixo de zeros para estofamento. Se você antecipar a necessidade de mais estofamento em ambos os lados do que a trinta ou então eu usei, você teria que aumentar o tamanho ... é evidente que você pode aumentá-lo dinamicamente, se necessário.
  • Seus comentários acima não totalmente de acordo com o código. Especificamente, se um personagem sinal foi devolvido, o comprimento retornado é maior do que solicitado (seus comentários dizer o contrário). Eu escolhi a acreditar que o código em vez dos comentários.
  • Eu fiz o meu método estático, uma vez que não requer nenhum estado da instância. Isso é uma coisa gosto pessoal - ymmv.

Além disso: a fim de imitar o comportamento do original (mas não dado nos comentários), o seguinte:

  • Se houver mais dígitos fracionários no valor de entrada de ajuste em escala, lança uma ArithmeticException Se houver mais dígitos inteiros no valor de entrada de ajuste em (escala LEN), a string retornada é maior que len. Se assinado é verdadeira, a string retornada será uma mais do que len

    • No entanto : if len é negativo, os retornos originais uma cadeia delimitada por vírgulas. Este lança uma 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();
       }
    
  • Outras dicas

    Se você está recebendo sua entrada como String para começar, por que você precisa convertê-lo para BigDecimal e voltar?

    Parece que seria muito mais rápido para encontrar a posição do ponto decimal, que ao comparar comprimento / escala e preencher a cadeia em conformidade.

    Eu concordo com ChssPly76 wrt a manipulação de cadeia manual.

    No entanto, se você estiver indo para ir abaixo da rota BigDecimal / DecimalFormat, você pode querer considerar compartilhar suas DecimalFormats em vez de criar um novo com cada iteração. Note-se que essas classes não são thread-safe, então se você estiver usando vários segmentos para fazer o seu processamento, você vai querer usar algo como o armazenamento ThreadLocal para manter um formatador per Thread.

    btw, você já testou este e encontrou o desempenho não aceitável ou você está apenas procurando a solução mais eficiente possível? que Donald Knuth diz sobre o tema do início otimização .

    Licenciado em: CC-BY-SA com atribuição
    Não afiliado a StackOverflow
    scroll top