我为各种替代方案进行了快速的微基准测试,似乎Günter's method和暴力检查之间几乎是平局。我更喜欢Günter的方法,因为它更加优美,但如果性能绝对关键,似乎暴力方法可能会略胜一筹。基准测试对于将返回true的索引和将返回false的索引分别运行每种方法一次。
暴力方法:
bool isDigit(String s, int idx) {
return s[idx] == "0"
|| s[idx] == "1"
|| s[idx] == "2"
|| s[idx] == "3"
|| s[idx] == "4"
|| s[idx] == "5"
|| s[idx] == "6"
|| s[idx] == "7"
|| s[idx] == "8"
|| s[idx] == "9";
}
0.045421617878512024微秒。
Günter的方法:
bool isDigit(String s, int idx) =>
"0".compareTo(s[idx]) <= 0 && "9".compareTo(s[idx]) >= 0;
0.054188391470161947 纳秒
您的代码单位方法:
int _zero = "0".codeUnits[0];
int _nine = "9".codeUnits[0];
bool isDigit(String s, int idx) =>
s.codeUnits[idx] >= _zero && s.codeUnits[idx] <= _nine;
0.6344102870896872微秒
通过存储s.codeUnits[idx]的结果(由于某种原因,虚拟机无法优化此过程,而其他方法中重复的s[idx]调用得到了优化,并且不比存储中间结果慢),可以将其提高2倍:
bool isDigit(String s, int idx) {
int cuIdx = s.codeUnits[idx];
cuIdx >= _zero && cuIdx <= _nine;
}
0.29245961607948817微秒
正则表达式方法:
RegExp digitRegExp = new RegExp(r'\d');
bool isDigit(String s, int idx) => s[idx].contains(digitRegExp);
4.812064808888846微秒
int.parse方法(非常缓慢):
bool isDigit(String s, int idx) {
bool isDigit = true;
try {
int.parse(s[2]);
} catch (e) {
isDigit = false;
}
return isDigit;
}
102.48526774276198微秒
.codeUnits
。它会创建一个包装字符串的对象,然后[idx]
只是执行string.codeUnitAt(idx)
。直接这样做即可。编译器可能会优化掉开销,但为什么要冒险呢?你也不想对同一索引调用两次codeUnitAt
- 使用变量。常量_zero和_nine应该是真正的常量,而不是懒惰初始化的可变全局变量,所以const _zero = 0x30; const _nine = 0x39;
这样编译器就知道它们是常量了。我会写成{ final char = s.codeUnitAt(idx); return _nine >= char && zero <= char; }
。 - lrn