【发布时间】:2021-05-20 13:44:33
【问题描述】:
答案可能存在于某个地方,但我找不到。我从我正在创建的算法中提出了这个问题。本质上是一个.contains(String s1, String s2),如果 s1 包含 s2,则返回 true,忽略希腊/英语字符差异。例如,字符串 'nai, course' 包含字符串 'ναι'。但是,这与我的问题无关。
String 的contains() 方法使用naive approach,我在我的算法中使用相同的方法。 contains() 本质上所做的就是用正确的参数调用存在于java.lang.String.class 中的static indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)。
当我对我的算法进行不同类型的基准测试和测试时,我删除了所有希腊语 - 英语逻辑,以查看仅使用英语字符串时它的运行速度有多快。而且速度较慢。比来自 JDK 的 s1.contains(s2) 慢大约 2 倍。
所以,我花时间将这个 indexOf 方法复制并粘贴到我的类中,并以与 JDK 调用字符串相同的方式调用它 1500 万次。
类如下:
public class TestIndex {
public static int fromJdk;
public static int fromMe;
public static void main(String[] args) {
String s1 = "papapatita";
String s2 = "patita";
final char[] s1Arr = s1.toCharArray();
int s1Length = s1Arr.length;
final char[] s2Arr = s2.toCharArray();
int s2Length = s2Arr.length;
long be4 = System.nanoTime();
for (int i = 0; i < 15_000_000; i++) {
fromJdk = s1.indexOf(s2);
// fromMe = indexOf(s1Arr, 0, s1Length, s2Arr, 0, s2Length, 0);
}
long after = System.nanoTime();
System.out.println((after - be4) / 1000000.0);
}
static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset,
int targetCount, int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first)
;
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++)
;
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
}
虽然fromJdk = s1.indexOf(s2); 没有评论,但结果是(在我的机器上)大约 150 毫秒。如果我评论这一行并留下 fromMe = indexOf(s1Arr, 0, s1Length, s2Arr, 0, s2Length, 0); 没有评论,我应该得到相同的结果。结果几乎是两倍。大约 300 毫秒。
那么,问题是为什么? indexOf 方法与 JDK 中存在的完全一样。我知道在 Java 中测量性能是一件很难的事情(可能是 JMH)?但是差别很大。来自 JDK 的 indexOf 是否在后台得到特殊处理?
如果答案在某处存在,请指出它。
【问题讨论】:
-
您使用的是哪个 Java 版本?
-
@GeorgeZ。 Kayaman 回答了什么,如果您查看 Java 11(最新 LTS 版本)的源代码,我会补充一下,您可以看到一个名为
@HotSpotIntrinsicCandidate的注释,表明 JVM 可以用更高效的机器代码替换该方法。我想这种行为存在于 Java 8 中,而且很可能在此之前也存在。 -
@MAnouti 也很高兴听到这个消息。感谢您的信息。我会记住的。
-
和here一样的问题