哪种方法对编译器更有效,并且是检查字符串是否为空的最佳实践?
检查字符串的长度是否== 0
检查字符串是否为空(strVar ==")
另外,答案是否取决于语言?
是的,这取决于语言,因为字符串存储在语言之间有所不同。
-
Pascal类型的字符串:Length = 0。
-
C风格的字符串:[0] == 0。
-
.NET:.IsNullOrEmpty。
等等。
在使用C样式(以空字符结尾)字符串的语言中,与""相比将更快。这是一个O(1)操作,而C风格字符串的长度为O(n)。
在将长度存储为字符串对象的一部分的语言(C#,Java等)中,检查长度也是O(1)。在这种情况下,直接检查长度会更快,因为它避免了构造新的空字符串的开销。
String.IsNullOrEmpty()仅适用于.net 2.0及更高版本,对于.net 1 / 1.1,我倾向于使用:
1 2 3 4
| if (inputString == null || inputString == String.Empty)
{
// String is null or empty, do something clever here. Or just expload.
} |
我使用String.Empty而不是"因为"会创建一个对象,而String.Empty不会-我知道它很小而琐碎,但是当我不需要它们时,id仍然不会创建对象! (资源)
In languages that use C-style (null-terminated) strings, comparing to"" will be faster
实际上,最好检查字符串中的第一个字符是否为' 0':
1 2 3 4 5
| char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
} |
在Perl中,还有第三个选项,即字符串未定义。这仅与C中的NULL指针有所不同,仅是因为您不会因访问未定义的字符串而出现分段错误。
在.Net中:
1
| string.IsNullOrEmpty( nystr ); |
字符串可以为null,因此.Length有时会引发NullReferenceException
阅读此主题后,我进行了一个小实验,得出了两个截然不同且有趣的发现。
考虑以下。
1
| strInstallString "1" string |
以上内容是从Visual Studio调试器的本地窗口复制的。以下三个示例中都使用相同的值。
if(strInstallString ==")=== if(strInstallString == string.Empty)
以下是这两个基本相同的情况在Visual Studio 2013调试器的反汇编窗口中显示的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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)没有明显的不同
1 2 3 4 5 6 7 8 9 10 11
| 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模块生成的机器代码清单中,我得出以下结论。
出于所有实际目的,针对空字符串文字和静态string.Empty属性进行的相等性测试都是相同的。这两个代码段之间的唯一区别是第一个移动指令的来源,并且两者都是相对于ds的偏移量,这意味着这两个代码段都引用了烘焙常量。
针对空字符串(如文字或string.Empty属性)进行相等性测试,以设置两个参数的函数调用,该函数调用通过返回零来指示不等式。我将这个结论基于几个月前我进行的其他测试,在这些测试中,我在托管/非托管除法和除法中遵循了自己的一些代码。在所有情况下,任何需要两个或更多参数的调用都将第一个参数放入寄存器ECX,将第二个放入寄存器EDX。我不记得随后的论点是如何通过的。但是,呼叫设置看起来更像是__fastcall而不是__stdcall。同样,期望的返回值总是显示在寄存器EAX中,这几乎是通用的。
测试字符串的长度将设置一个单参数函数调用,该函数返回1(在寄存器EAX中),恰好是被测试的字符串的长度。
鉴于立即可见的机器代码几乎完全相同,所以我能想到的唯一原因是,在Shinny报告的字符串长度上字符串相等性的性能更好,原因是执行比较的二元函数明显更好比从字符串实例读取长度的单参数函数优化。
结论
原则上,我避免将空字符串作为文字进行比较,因为空字符串文字在源代码中可能会变得模棱两可。为此,我的.NET帮助器类将空字符串长期定义为常量。尽管我使用string.Empty进行直接的内联比较,但该常量保留了定义其他值为空字符串的常量的条件,因为无法将一个常量分配给string.Empty作为其值。
本练习将一劳永逸地解决我可能对与string.Empty或助手类定义的常数进行比较的成本(如果有)的任何担忧。
但是,它也提出了一个令人困惑的问题来替代它。为什么与string.Empty比较比测试字符串的长度更有效?还是因为通过实现循环的方式,Shinny使用的测试是无效的? (我很难相信,但是话又说回来,我以前也被骗了,因为我敢肯定你也有!)
长期以来,我一直认为system.string对象被视为字符串,从根本上类似于我们早已从COM得知的已建立的Basic String(BSTR)。
假设您的问题是.NET:
如果还要验证字符串是否为空,请使用IsNullOrEmpty,如果已经知道字符串不为空,例如在检查TextBox.Text等时,请勿使用IsNullOrEmpty,然后出现问题。
因此,我认为String.Length的性能不如字符串比较。
我对其进行了事件测试(我也对C#进行了测试,结果相同):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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 |
结果:
1 2 3 4 5
| Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2 |
这意味着比较比字符串长度检查更重要。
在Java 1.6中,String类具有一个新方法isEmpty
还有一个具有isBlank方法的Jakarta Commons库。空白定义为仅包含空格的字符串。
同样,如果不知道语言,就无法分辨。
但是,我建议您选择对随后的维护程序员最有意义并且必须维护您的工作的技术。
我建议编写一个明确执行所需功能的函数,例如
1
| #define IS_EMPTY(s) ((s)[0]==0) |
或可比。现在毫无疑问,您正在检查。
@内森
Actually, it may be better to check if the first char in the string is '\0':
我几乎提到了这一点,但最终还是把它遗漏了,因为用空字符串调用strcmp()并直接检查字符串中的第一个字符都是O(1)。基本上,您只需要为额外的函数调用付费,这非常便宜。但是,如果您确实需要绝对的最佳速度,则可以直接进行从第一个字符到0的比较。
老实说,我一直使用strlen() == 0,因为我从未编写过一个实际上是可衡量的性能问题的程序,而且我认为这是表达支票最易读的方式。
对于C字符串,
会比任何一个都快
要么
因为您将避免函数调用的开销。
In this case, directly checking the length is faster, because it avoids the overhead of constructing the new empty string.
@DerekPark:并非总是如此。"是一个字符串文字,因此,在Java中,几乎可以肯定它已经被嵌入。
实际上,IMO确定的最佳方法是字符串类的IsNullOrEmpty()方法。
http://msdn.microsoft.com/zh-cn/library/system.string.isnullorempty。
更新:我假设.Net在其他语言中可能有所不同。