Самый быстрый способ дополнить число в Java определенным количеством цифр - PullRequest
2 голосов
/ 07 июня 2010

Пытаюсь создать хорошо оптимизированный бит кода, чтобы создать количество X-цифр в длине (где X читается из файла свойств времени выполнения) на основе сгенерированного БД порядкового номера (Y), который затем использовал имя папки при сохранении файла.

Пока что я выдвинул три идеи, самая быстрая из которых - последняя, ​​но я был бы признателен за любые советы, которые люди могут дать по этому поводу ...

1) Создание экземпляра StringBuilder с начальной емкостью X. Добавьте Y. Когда длина

2) Создание экземпляра StringBuilder с начальной емкостью X. Пока длина

3) Создайте новый int для Math.pow (10, X) и добавьте Y. Используйте String.valueOf () для нового номера, а затем подставьте его (1).

Второй, очевидно, можно разбить на секции внешнего и внутреннего цикла.

Итак, есть какие-нибудь советы? Используя цикл for из 10 000 итераций, я получаю аналогичные временные интервалы от первых двух, а третий метод работает примерно в десять раз быстрее. Это кажется правильным?

Полный код метода тестирования ниже ...

    // 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 ]

7 голосов
/ 07 июня 2010

Я бы прекратил делать оптимизации на основе микропроцессоров и пошел бы на что-то, что выглядит элегантно в коде, например String.format("%0"+numDigits+"d", testNumber)

2 голосов
/ 07 июня 2010

Использовать String.format ("% 0 [length] d", i)

Для длины 8 это будет

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

Это медленнее, но время, потраченное на ввод иотладка более сложного кода, вероятно, превысит общее дополнительное время, когда-либо использованное во время выполнения.

Фактически, если вы сложите все человеко-часы, уже потраченные на обсуждение этого, скорее всего, это превысит экономию времени выполнения набольшой фактор.

1 голос
/ 07 июня 2010

Вот решение, которое в основном совпадает с вашим StringBuilder с двумя оптимизациями:

  1. напрямую записывает в массив в обход издержек StringBuilder
  2. Выполняет операции в обратном порядке. вместо insert (0), который запрашивает матричная копия каждый раз

Он также делает предположения, что 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"

Конечно, как уже упоминали другие, микро-бенчмаркинг невероятно труден / «утомителен» со всей оптимизацией, выполняемой ВМ, и невозможностью их контролировать.

1 голос
/ 07 июня 2010

Вставка дополнительных символов по одному, очевидно, медленная. Если производительность действительно так важна, вы можете вместо этого использовать предопределенные строковые константы из длин 1. 1.n-1 (где n - наибольшая ожидаемая длина), хранящихся в ArrayList по соответствующим индексам.

Если n очень велико, по крайней мере, вы все равно можете вставлять большие куски вместо одиночных символов.

Но в целом, как отмечали и другие, оптимизация возможна только в том случае, если вы профилировали свое приложение в реальных условиях и обнаружили, какой конкретный фрагмент кода является узким местом. Затем вы можете сосредоточиться на этом (и, разумеется, снова профиль, чтобы убедиться, что ваши изменения действительно улучшают производительность).

0 голосов
/ 07 июня 2010

Эта, вероятно, связанная ссылка обсуждает многие способы сделать это. Я бы порекомендовал параметр Apache, StringUtils, он может быть или не быть самым быстрым, но обычно он один из самых простых для понимания, и в нем выбито) & ## @, поэтому он, вероятно, не сломается. в каком-то непредвиденном крайнем случае. ;)

...