将Java中的数字填充到一定数量的数字的最快方法

时间:2010-06-07 12:59:47

标签: java performance substring stringbuilder digits

尝试根据DB生成的序列号(Y)创建一个经过良好优化的代码位来创建长度为X位的数字(其中X是从运行时属性文件中读取的),然后是保存文件时使用文件夹名称。

到目前为止,我已经提出了三个想法,其中最快的是最后一个,但我很感激人们对此有任何建议......

1)实例化具有初始容量X的StringBuilder。追加Y.而长度< X,在零位置插入零。

2)实例化具有初始容量X的StringBuilder。 X,追加零。根据StringBuilder值创建DecimalFormat,然后在需要时格式化数字。

3)创建Math.pow(10,X)的新int并添加Y.对新数字使用String.valueOf(),然后对其进行子串(1)。

第二个显然可以分为外环和内环部分。

那么,有什么提示吗?使用10,000次迭代的for循环,我从前两个获得类似的时序,第三个方法大约快十倍。这看起来是否正确?

以下完整的测试方法代码......

    // Setup test variables
    int numDigits = 9;
    int testNumber = 724;
    int numIterations = 10000;
    String folderHolder = null;
    DecimalFormat outputFormat = new DecimalFormat( "#,##0" );

    // StringBuilder test
    long before = System.nanoTime();
    for ( int i = 0; i < numIterations; i++ )
    {
        StringBuilder sb = new StringBuilder( numDigits );
        sb.append( testNumber );
        while ( sb.length() < numDigits )
        {
            sb.insert( 0, 0 );
        }

        folderHolder = sb.toString();
    }
    long after = System.nanoTime();
    System.out.println( "01: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );

    // DecimalFormat test
    before = System.nanoTime();
    StringBuilder sb = new StringBuilder( numDigits );
    while ( sb.length() < numDigits )
    {
        sb.append( 0 );
    }
    DecimalFormat formatter = new DecimalFormat( sb.toString() );
    for ( int i = 0; i < numIterations; i++ )
    {
        folderHolder = formatter.format( testNumber );
    }
    after = System.nanoTime();
    System.out.println( "02: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );

    // Substring test
    before = System.nanoTime();
    int baseNum = (int)Math.pow( 10, numDigits );
    for ( int i = 0; i < numIterations; i++ )
    {
        int newNum = baseNum + testNumber;
        folderHolder = String.valueOf( newNum ).substring( 1 );
    }
    after = System.nanoTime();
    System.out.println( "03: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );

5 个答案:

答案 0 :(得分:7)

我会停止基于微基准测试进行优化,并寻找代码优雅的东西,例如String.format("%0"+numDigits+"d", testNumber)

答案 1 :(得分:2)

使用String.format(“%0 [length] d”,i)

长度为8时,它将是

String out = String.format("%08d", i);

速度较慢,但​​输入和调试更复杂代码所花费的时间可能会超过执行期间使用的总时间。

事实上,如果你把讨论过的所有工时加起来,很可能会超过执行时间的节省。

答案 2 :(得分:1)

逐个插入填充字符显然很慢。如果性能确实非常重要,您可以使用预定义的字符串常量1..n-1(其中 n 是最大预期长度),存储在相应索引的ArrayList中。

如果 n 非常大,至少你仍然可以插入更大的块而不是单个字符。

但总体而言,正如其他人所指出的那样,只有在真实情况下对应用程序进行了分析并发现哪个特定代码是瓶颈时,优化才是可行的。然后你可以专注于那个(当然还有配置文件,以验证你的更改是否真正提高了性能)。

答案 3 :(得分:1)

这是一个与StringBuilder基本相同的解决方案,有两个优化:

  1. 它直接写入数组 绕过StringBuilder开销
  2. 它反向执行操作 而不是插入(0),这是requries 每次都是一个arraycopy
  3. 它还假设numDigits将> =所需的实际字符,但会正确处理负数:

    before = System.nanoTime();
    String arrString=null;
    for ( int j = 0; j < numIterations; j++ ){
      char[] arrNum = new char[numDigits];
      int i = numDigits-1;
      boolean neg = testNumber<0;
      for(int tmp = neg?-testNumber:testNumber;tmp>0;tmp/=10){
        arrNum[i--] = (char)((tmp%10)+48);
      }
      while(i>=0){
        arrNum[i--]='0';
      }
      if(neg)arrNum[0]='-';
      arrString = new String(arrNum);
    }
    after = System.nanoTime();
    System.out.println( "04: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + arrString + "\"" );
    

    这种方法在我的机器上的样本表现优于阴性,并且与阳性结果相当:

    01: 18,090,933 nanoseconds
    Sanity check: Folder = "000000742"
    02: 22,659,205 nanoseconds
    Sanity check: Folder = "000000742"
    03: 2,309,949 nanoseconds
    Sanity check: Folder = "000000742"
    04: 6,380,892 nanoseconds
    Sanity check: Folder = "000000742"
    
    01: 14,933,369 nanoseconds
    Sanity check: Folder = "0000-2745"
    02: 21,685,158 nanoseconds
    Sanity check: Folder = "-000002745"
    03: 3,213,270 nanoseconds
    Sanity check: Folder = "99997255"
    04: 1,255,660 nanoseconds
    Sanity check: Folder = "-00002745"
    

    编辑:我注意到你的测试已经在迭代循环中恢复了一些对象,我没有在我的中完成(例如不在子字符串版本中重新计算baseNum)。当我将测试更改为一致时(不重新生成任何对象/计算,我的版本比你的更好:

    01: 18,377,935 nanoseconds
    Sanity check: Folder = "000000742"
    02: 69,443,911 nanoseconds
    Sanity check: Folder = "000000742"
    03: 6,410,263 nanoseconds
    Sanity check: Folder = "000000742"
    04: 996,622 nanoseconds
    Sanity check: Folder = "000000742"
    

    当然,正如其他人所提到的那样,基准测试非常难以实现,并且无法控制它们。

答案 4 :(得分:0)

This probably related链接讨论了许多方法。我会推荐Apache选项,StringUtils,它可能是也可能不是绝对最快的,但它通常是最容易理解的,并且已经有了&amp; ## @捣乱了,所以它可能不会打破一些不可预见的边缘案件。 ;)