第一个更新:在生产环境(不建议使用)中尝试此方法之前,请先阅读以下内容:http : //www.javaspecialists.eu/archive/Issue237.html 从Java 9开始,上述解决方案将不再起作用,因为现在Java默认将字符串存储为byte []。
第二次更新:截至2016年10月25日,在我的AMDx64 8core和源1.8上,使用'charAt'和字段访问之间没有区别。看来jvm已经过充分优化,可以内联和精简任何'string.charAt(n)'调用。
这完全取决于String被检查的时间。如问题所述,如果用于长字符串,则检查字符串的最快方法是使用反射访问char[]字符串的支持。
在64种AMD Phenom II 4核心955 @ 3.2 GHZ(在客户端模式和服务器模式下)上使用9种不同技术(见下文!)对JDK 8(win32和win64)进行完全随机化的基准测试表明,使用String.charAt(n)速度最快字符串,reflection用于访问字符串支持数组的字符串的速度几乎是大型字符串的两倍。
本实验
尝试了9种不同的优化技术。
所有字符串内容都是随机的
以0、1、2、4、8、16等开头的字符串大小为2的倍数的形式进行测试。
每个字符串大小进行1000次测试
每次将测试随机排列。换句话说,每次测试都以随机顺序进行,超过1000次。
整个测试套件都向前和向后进行,以显示JVM预热对优化和时间的影响。
整个套件执行两次,一次在-client模式下,另一个在-server模式下。
结论
-客户端模式(32位)
对于长度为1到256个字符的字符串,呼叫string.charAt(i)胜出率是平均每秒处理1340万到5.88亿个字符。
而且,总体来说,客户端(客户端)和服务器(服务器)的速度提高了5.5%,如下所示:
for (int i = 0; i < data.length(); i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
而不是像这样带有局部最终长度变量:
final int len = data.length();
for (int i = 0; i < len; i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
对于512到256K字符长的长字符串,使用反射访问String的后备数组最快。此技术的速度几乎是String.charAt(i)的两倍(快 178%)。在此范围内的平均速度为每秒11.11亿个字符。
必须提前获取字段,然后可以在库中的不同字符串上重新使用它。有趣的是,与上面的代码不同,使用Field访问,使用本地最终长度变量比在循环检查中使用“ chars.length”要快9%。可以将现场访问设置为最快的方法如下:
final Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
try {
final char[] chars = (char[]) field.get(data);
final int len = chars.length;
for (int i = 0; i < len; i++) {
if (chars[i] <= ' ') {
doThrow();
}
}
return len;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
-server模式下的特殊注释
在我的AMD 64机器上的64位Java机器上,在服务器模式下以服务器模式获得32个字符长的字符串后,现场访问开始获胜。在客户端模式下,只有512个字符的长度才能看到。
同样值得一提的是,当我在服务器模式下运行JDK 8(32位构建)时,无论大小字符串,整体性能都降低了7%。这是JDK 8早期版本的内部版本121 Dec 2013。因此,目前看来,32位服务器模式比32位客户端模式慢。
话虽如此……似乎唯一值得调用的服务器模式是在64位计算机上。否则实际上会影响性能。
对于在-server modeAMD64上运行的32位版本,我可以这样说:
总的来说,String.charAt(i)是明显的赢家。尽管介于8到512个字符之间,但在“新”,“重用”和“领域”中还是赢家。
在客户端模式下,String.charAt(i)的速度提高了45%
在客户端模式下,大型字符串的字段访问速度是后者的两倍。
同样值得一提的是,String.chars()(Stream和并行版本)是破产。比其他任何方式都要慢。该StreamsAPI是执行一般的字符串操作一个相当缓慢的方式。
愿望清单
Java String的谓词可以接受优化的方法,例如contains(predicate),forEach(consumer),forEachWithIndex(consumer)。因此,无需用户知道长度或重复调用String方法,这些方法就可以帮助解析库beep-beep beep加速。
继续做梦:)
快乐的弦!
〜SH
该测试使用以下9种方法测试字符串是否存在空格:
“ charAt1”-检查字符串是否包含通常的方式:
int charAtMethod1(final String data) {
final int len = data.length();
for (int i = 0; i < len; i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return len;
}
“ charAt2” –与上面相同,但使用String.length()代替了对长度进行最后的本地int
int charAtMethod2(final String data) {
for (int i = 0; i < data.length(); i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return data.length();
}
“流”-使用新的JAVA-8 String的IntStream并通过它进行检查
int streamMethod(final String data, final IntPredicate predicate) {
if (data.chars().anyMatch(predicate)) {
doThrow();
}
return data.length();
}
“ streamPara”-同样,但是OH-LA-LA-并行!
// avoid this at all costs
int streamParallelMethod(final String data, IntPredicate predicate) {
if (data.chars().paralle