コンパイラにとってより効率的で、文字列が空白かどうかをチェックするためのベスト プラクティスはどれですか?
- 文字列の長さ == 0 かどうかの確認
- 文字列が空かどうかの確認 (strVar == "")
また、答えは言語によって異なりますか?
コンパイラにとってより効率的で、文字列が空白かどうかをチェックするためのベスト プラクティスはどれですか?
また、答えは言語によって異なりますか?
はい、文字列のストレージは言語によって異なるため、言語によって異なります。
Length = 0
.[0] == 0
..IsNullOrEmpty
.等。
C スタイル (null で終わる) 文字列を使用する言語では、 との比較""
が高速になります。これは O(1) 操作ですが、C スタイルの文字列の長さは O(n) です。
文字列オブジェクトの一部として長さを格納する言語 (C#、Java など) では、長さのチェックも O(1) です。この場合、新しい空の文字列を作成するオーバーヘッドを回避できるため、長さを直接確認する方が高速です。
C スタイル (ヌル終了) の文字列を使用する言語では、"" との比較が高速になります。
実際には、文字列の最初の文字が '\0' であるかどうかを確認する方がよい場合があります。
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
Perl には、文字列が未定義であるという 3 番目のオプションがあります。これは、未定義の文字列にアクセスしてもセグメンテーション違反が発生しないという理由だけで、C の NULL ポインターとは少し異なります。
.Net では:
string.IsNullOrEmpty( nystr );
文字列は null になる可能性があるため、.Length は NullReferenceException をスローすることがあります
String.IsNullOrEmpty()
.net 2.0 以降でのみ動作します。.net 1/1.1 の場合は、次を使用する傾向があります。
if (inputString == null || inputString == String.Empty)
{
// String is null or empty, do something clever here. Or just expload.
}
"" ではなく String.Empty を使用します。これは、"" がオブジェクトを作成するのに対し、String.Empty はそうではないためです。小さくて些細なことは知っていますが、必要のないオブジェクトは作成しません。(ソース)
このスレッドを読んだ後、ちょっとした実験を行ったところ、2 つの異なる興味深い結果が得られました。
以下を検討してください。
strInstallString "1" string
上記は、Visual Studio デバッガーのローカル ウィンドウからコピーされたものです。次の 3 つの例では、すべて同じ値が使用されています。
if ( strInstallString == "" ) === if ( strInstallString == string.Empty )
以下は、これら 2 つの基本的に同一のケースについて、Visual Studio 2013 デバッガーの逆アセンブリ ウィンドウに表示されるコードです。
if ( strInstallString == "" )
003126FB mov edx,dword ptr ds:[31B2184h]
00312701 mov ecx,dword ptr [ebp-50h]
00312704 call 59DEC0B0 ; On return, EAX = 0x00000000.
00312709 mov dword ptr [ebp-9Ch],eax
0031270F cmp dword ptr [ebp-9Ch],0
00312716 sete al
00312719 movzx eax,al
0031271C mov dword ptr [ebp-64h],eax
0031271F cmp dword ptr [ebp-64h],0
00312723 jne 00312750
if ( strInstallString == string.Empty )
00452443 mov edx,dword ptr ds:[3282184h]
00452449 mov ecx,dword ptr [ebp-50h]
0045244C call 59DEC0B0 ; On return, EAX = 0x00000000.
00452451 mov dword ptr [ebp-9Ch],eax
00452457 cmp dword ptr [ebp-9Ch],0
0045245E sete al
00452461 movzx eax,al
00452464 mov dword ptr [ebp-64h],eax
00452467 cmp dword ptr [ebp-64h],0
0045246B jne 00452498
if ( strInstallString == string.Empty ) に大きな違いはない
if ( strInstallString.Length == 0 )
003E284B mov ecx,dword ptr [ebp-50h]
003E284E cmp dword ptr [ecx],ecx
003E2850 call 5ACBC87E ; On return, EAX = 0x00000001.
003E2855 mov dword ptr [ebp-9Ch],eax
003E285B cmp dword ptr [ebp-9Ch],0
003E2862 setne al
003E2865 movzx eax,al
003E2868 mov dword ptr [ebp-64h],eax
003E286B cmp dword ptr [ebp-64h],0
003E286F jne 003E289C
.NET Framework バージョン 4.5 の NGEN モジュールによって生成された上記のマシン コード リストから、次の結論を導き出します。
空の文字列リテラルと System.string クラスの static string.Empty プロパティに対する等価性のテストは、実際にはすべて同じです。2 つのコード スニペットの唯一の違いは、最初の move 命令のソースです。どちらも ds に対するオフセットであり、どちらも組み込みの定数を参照していることを意味します。
リテラルまたは string.Empty プロパティのいずれかとして、空の文字列に対して等しいかどうかをテストすると、引数が 2 つの関数呼び出しが設定されます。これは、ゼロを返すことによって不等値を示します。この結論は、数か月前に実行した他のテストに基づいています。このテストでは、マネージド/アンマネージドの境界を越えて独自のコードをたどりました。いずれの場合も、2 つ以上の引数を必要とする呼び出しでは、最初の引数がレジスタ ECX に配置され、2 番目の引数がレジスタ EDX に配置されます。その後の引数がどのように渡されたかは覚えていません。それにもかかわらず、呼び出しセットアップは __stdcall よりも __fastcall に似ていました。同様に、期待される戻り値は常にレジスタ EAX に表示されますが、これはほぼ普遍的です。
文字列の長さをテストすると、引数が 1 つの関数呼び出しが設定されます。この関数呼び出しは、(レジスタ EAX で) 1 を返します。これは、たまたまテストされている文字列の長さです。
すぐに見える機械語コードがほぼ同じであることを考えると、 Shinnyによって報告された文字列の長さに対する文字列の等価性のより優れたパフォーマンスを説明できる唯一の理由は、比較を実行する 2 つの引数関数が大幅に優れていることです。文字列インスタンスから長さを読み取る引数が 1 つの関数よりも最適化されています。
結論
原則として、空の文字列をリテラルとして比較することは避けています。これは、空の文字列リテラルがソース コード内であいまいに見える可能性があるためです。そのために、私の .NET ヘルパー クラスでは長い間、空の文字列を定数として定義してきました。直接のインライン比較にstring.Emptyを使用していますが、定数には値としてstring.Emptyを割り当てることができないため、定数は、値が空の文字列である他の定数を定義するために保持されます。
この演習では、 string.Emptyまたはヘルパー クラスで定義された定数のいずれかと比較することによるコストについて懸念がある場合は、これで解決します。
ただし、それを置き換えるには不可解な問題も発生します。string.Emptyと比較すると、文字列の長さをテストするよりも効率的なのはなぜですか? それとも、ループが実装されているため、Shinny が使用するテストが無効になっているのでしょうか。(信じがたいことですが、あなたもそうだと思いますが、私も以前騙されたことがあります!)
私は長い間、system.stringオブジェクトはカウント文字列であると考えてきました。これは、基本的に、COM で長い間知られている、長い間確立された Basic String (BSTR) に似ています。
Java 1.6 では、String クラスに新しいメソッド [isEmpty] 1が追加されました。
[isBlank] 2メソッドを持つ Jakarta commons ライブラリもあります。空白は、空白のみを含む文字列として定義されます。
あなたの質問が.NETであると仮定します:
null に対しても文字列を検証する場合は、IsNullOrEmpty を使用します。たとえば、TextBox.Text などをチェックするときなど、文字列が null ではないことがわかっている場合は、IsNullOrEmpty を使用しないでください。
したがって、私の意見では、 String.Length は文字列比較よりもパフォーマンスが低くなります。
イベントでテストしました(C#でもテストしましたが、同じ結果です):
Module Module1
Sub Main()
Dim myString = ""
Dim a, b, c, d As Long
Console.WriteLine("Way 1...")
a = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString = ""
Next
b = Now.Ticks
Console.WriteLine("Way 2...")
c = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString.Length = 0
Next
d = Now.Ticks
Dim way1 = b - a, way2 = d - c
Console.WriteLine("way 1 took {0} ticks", way1)
Console.WriteLine("way 2 took {0} ticks", way2)
Console.WriteLine("way 1 took {0} ticks more than way 2", way1 - way2)
Console.Read()
End Sub
End Module
結果:
Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2
つまり、比較は文字列の長さのチェックよりもはるかに重要です。
C弦の場合、
if (s[0] == 0)
どちらよりも速くなります
if (strlen(s) == 0)
また
if (strcmp(s, "") == 0)
関数呼び出しのオーバーヘッドを回避できるからです。
実際、IMO が決定する最善の方法は、文字列クラスの IsNullOrEmpty() メソッドです。
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.
更新: .Net を想定していましたが、他の言語では、これは異なる可能性があります。
繰り返しますが、言語を知らなければ、それを伝えることは不可能です。
ただし、後で作業を保守する必要がある保守プログラマーにとって最も理にかなった手法を選択することをお勧めします。
次のように、必要なことを明示的に行う関数を作成することをお勧めします
#define IS_EMPTY(s) ((s)[0]==0)
または同等。今、あなたがチェックしていることに疑いの余地はありません。
この場合、新しい空の文字列を作成するオーバーヘッドが回避されるため、長さを直接確認する方が高速です。
@DerekPark: それは常に正しいとは限りません。"" は文字列リテラルであるため、Java ではほぼ確実に既にインターンされています。
@ネイサン
実際には、文字列の最初の文字が '\0' であるかどうかを確認する方がよい場合があります。
私はほとんどそれを言及しましたが、空の文字列で呼び出すことstrcmp()
と、文字列の最初の文字を直接チェックすることは両方とも O(1) であるため、省略してしまいました。基本的に、追加の関数呼び出しに対して支払うだけで、かなり安価です。ただし、絶対的な最高速度が本当に必要な場合は、最初の文字と 0 を直接比較することをお勧めします。
正直なところ、これが実際に測定可能なパフォーマンスの問題であるプログラムを作成したことがないstrlen() == 0
ため、私は常に を使用します。これがチェックを表現する最も読みやすい方法だと思います。