Javaの数字を特定の数字にパッドする最速の方法
-
24-10-2019 - |
質問
DB生成されたシーケンス番号(y)に基づいて、X-Digitsの長さ(xが実行時プロパティファイルから読み取られる)を作成するために、適切に最適なビットのコードを作成しようとしています。 -nameファイルを保存するとき。
私はこれまでに3つのアイデアを思いつきましたが、その最速は最後のアイデアですが、人々がこれについて持っているかもしれないアドバイスに感謝します...
1)初期容量XでStringBuilderをインスタンス化します。Yを追加します。
2)初期容量XのStringBuilderをインスタンス化します。長さ<x、ゼロを追加します。 StringBuilder値に基づいてDecimalFormatを作成し、必要なときに数値をフォーマットします。
3)Math.Pow(10、x)の新しいintを作成し、yを追加します。新しい番号でstring.valueof()を使用してから(1)substring(1)を使用します。
2番目のものは、明らかにループ外のセクションと内部ループのセクションに分割できます。
それで、ヒントはありますか? 10,000回の反復のループを使用すると、最初の2つから同様のタイミングが得られ、3番目の方法は約10倍高速です。これは正しいようですか?
以下の完全なテストメソッドコード...
// 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 + "\"" );
解決
マイクロベンチマークに基づいて最適化をやめ、エレガントなコードワイズのように見えるものを探します。 String.format("%0"+numDigits+"d", testNumber)
他のヒント
string.format( "%0 [length] d"、i)を使用します
8の長さはそうでしょう
String out = String.format("%08d", i);
遅くなりますが、タイピングとデバッグに費やす時間は、より複雑なコードをデバッグすることで、実行中に使用される合計時間を超えるでしょう。
実際、すでにこれについて議論することに費やしたすべての人時間を合計すると、おそらく大きな要因による実行時間の節約を超えています。
パディングキャラクターを1つずつ挿入するのは明らかに遅いです。パフォーマンスが本当に大きな懸念事項である場合、代わりにlengts 1..n-1の事前定義された文字列定数を使用できます(ここで n 対応するインデックスのアレイリストに保存されている最大の予想される長さ)。
もしも n 非常に大きいですが、少なくともシングルチャーの代わりに大きなチャンクに挿入することができます。
しかし、全体として、他の人も指摘したように、実際の状況下でアプリケーションを紹介し、どの特定のコードがボトルネックであるかを見つけた場合にのみ最適化が実行可能です。その後、それに焦点を合わせることができます(もちろん、変更を再度プロファイルして、変更が実際にパフォーマンスを改善することを確認します)。
これは、2つの最適化を備えたStringBuilderと基本的に同じものです。
- StringBuilderのオーバーヘッドをバイパスする配列に直接書き込みます
- 挿入(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"
編集: 私はあなたのテストが私のものではなかった反復ループ内のいくつかのオブジェクトの一部を再び繰り返していることに気付きました(サブストリングバージョンでゼンセンを再計算しないなど)。テストを一貫して変更したとき(オブジェクト /計算を再開しないでください。私のバージョンはあなたよりも優れていました。
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"
もちろん、他の人が言及したように、マイクロベンチマークは非常に難しい /「Fudgy」は、VMによって実行されるすべての最適化とそれらを制御できないことです。
これはおそらく関連しています リンクは、それを行う方法の多くについて説明します。私はapacheオプション、stringutilsをお勧めします、それは絶対的な最速であるかもしれませんが、それは通常最も簡単なものの1つであり、それからwadeded@を持っていたので、おそらく壊れないでしょう予期せぬエッジケースでは。 ;)