Java 为什么String.equalsIgnoreCase这么慢

Java 为什么String.equalsIgnoreCase这么慢,java,string,performance,ignore-case,Java,String,Performance,Ignore Case,我在采访中遇到了一个问题,写了一个方法来检查类似的单词,而不考虑字符大小写 我用每对字符的ASCII值之差来回答这个问题。但是在家里,当我在String.class中实际实现它时,我感到不安-为什么它是这样实现的 我试图通过这种方式在内置方法和自定义方法之间进行比较- public class EqualsIgnoreCase { public static void main(String[] args) { String str1 = "Srimant @$ Sah

我在采访中遇到了一个问题,写了一个方法来检查类似的单词,而不考虑字符大小写

我用每对字符的ASCII值之差来回答这个问题。但是在家里,当我在String.class中实际实现它时,我感到不安-为什么它是这样实现的

我试图通过这种方式在内置方法和自定义方法之间进行比较-

public class EqualsIgnoreCase {

    public static void main(String[] args) {
        String str1 = "Srimant @$ Sahu 959s";
        String str2 = "sriMaNt @$ sAhu 959s";

        System.out.println("Avg millisecs with inbuilt () - " + averageOfTenForInbuilt(str1, str2));
        System.out.println("\nAvg millisecs with custom () - " + averageOfTenForCustom(str1, str2));
    }

    public static int averageOfTenForInbuilt(String str1, String str2) {
        int avg = 0;
        for (int itr = 0; itr < 10; itr++) {
            long start1 = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                str1.equalsIgnoreCase(str2);
            }
            avg += System.currentTimeMillis() - start1;
        }
        return avg / 10;
    }

    public static int averageOfTenForCustom(String str1, String str2) {
        int avg = 0;
        for (int itr = 0; itr < 10; itr++) {
            long start2 = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                isEqualsIgnoreCase(str1, str2);
            }
            avg += System.currentTimeMillis() - start2;
        }
        return avg / 10;
    }

    public static boolean isEqualsIgnoreCase(String str1, String str2) {
        int length = str1.length();
        if (str2.length() != length) {
            return false;
        }

        for (int i = 0; i < length; i++) {
            char ch1 = str1.charAt(i);
            char ch2 = str2.charAt(i);

            int val = Math.abs(ch1 - ch2);
            if (val != 0) {
                if (isInAlphabetsRange(ch1, ch2)) {
                    if (val != 32) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }
        return true;
    }

    public static boolean isInAlphabetsRange(char ch1, char ch2) {
        return (((ch1 <= 122 && ch1 >= 97) || (ch1 <= 90 && ch1 >= 65)) && ((ch2 <= 122 && ch2 >= 97) || (ch2 <= 90 && ch2 >= 65)));
    }

}
公共类EqualsIgnoreCase{
公共静态void main(字符串[]args){
字符串str1=“Srimant@$Sahu 959s”;
字符串str2=“sriMaNt@$sAhu 959s”;
System.out.println(“内置的平均毫秒数()-”+内置的平均数(str1,str2));
System.out.println(“\nAvg毫秒带自定义()-”+平均循环次数(str1,str2));
}
公共静态int-averageoftextrinbuild(字符串str1、字符串str2){
int平均值=0;
对于(int-itr=0;itr<10;itr++){
long start1=System.currentTimeMillis();
对于(int i=0;i<100000;i++){
str1.等信号病例(str2);
}
avg+=System.currentTimeMillis()-start1;
}
返回平均值/10;
}
公共静态int平均值的整数循环(字符串str1,字符串str2){
int平均值=0;
对于(int-itr=0;itr<10;itr++){
long start2=System.currentTimeMillis();
对于(int i=0;i<100000;i++){
isEqualsIgnoreCase(str1,str2);
}
avg+=System.currentTimeMillis()-start2;
}
返回平均值/10;
}
公共静态布尔isEqualsIgnoreCase(字符串str1、字符串str2){
int length=str1.length();
如果(str2.length()!=长度){
返回false;
}
for(int i=0;i
输出-

内置的平均毫秒数()-14

自定义的平均毫秒数()-5

我发现,由于大量的检查和方法调用,内置方法正在影响效率。 这种实施背后有什么具体原因吗?还是我在逻辑上遗漏了什么


如有任何建议,将不胜感激

您的例程仅处理ASCII字符。system one处理所有unicode字符

考虑以下示例:

public class Test {

    public static void main(String[] args) {
        System.out.println((int) 'ě'); // => 283
        System.out.println((int) 'Ě'); // => 282 
    }

}

我认为检查

String1.等效信号案例(String2)

所提供的一个具有更好的字符可接受性,它可以接受Unicode中包含的所有类型的字符值;但是您试图通过自定义代码了解的是,您只比较英文字母字符


因此,我认为,根据你文章的评论员Pavel Horel的观点,由于它为各种Unicode字符之间的比较提供了复杂的方法,这可能需要更多的时间。

你的方法在很多方面都是不正确的。例如,它认为“!”等于“B”,“B”等于“1”,但“!”不等于“1”(因此它不像我们期望的equals方法那样是可传递的)

是的,很容易为该方法编写一个更快更简单的错误实现。一个公平的挑战是编写一个正确的,即正确处理JDK实现所做的所有参数

您可能还希望查看以获得更可靠的性能度量。

这可能不是唯一的原因,但您的解决方案实际上并不适用于所有可能的字符串这一事实无疑是一个因素

有些(恼人的)区域设置中,两个字符可能具有相同的大写字母,但不具有相同的小写字母。因此,为了工作(大多数情况下,请参见Turkish),规范实现必须在小写和大写两种情况下对字符串char进行比较


您的实现可能99%都是完美的,特别是当您只需要处理英语语言环境时,但不幸的是核心库实现不能做出这样的假设。

我认为String.java中的这个练习是相关的:

if (ignoreCase) {
    // If characters don't match but case may be ignored,
    // try converting both characters to uppercase.
    // If the results match, then the comparison scan should
    // continue.
    char u1 = Character.toUpperCase(c1);
    char u2 = Character.toUpperCase(c2);
    if (u1 == u2) {
        continue;
    }
    // Unfortunately, conversion to uppercase does not work properly
    // for the Georgian alphabet, which has strange rules about case
    // conversion.  So we need to make one last check before
    // exiting.
    if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
        continue;
    }
}

尝试先调用
AverageOftEnforrcustom
,然后调用
AverageOftEnforInBuild
:由于JVM启动,实际结果可能会有所不同。小写和大写之间的转换不是一项简单的任务。您仅使用基本ASCII拉丁字符(字符距离32)。但是,当您使用整个Unicode集时,问题相当复杂@sp00m交替调用时没有显著差异!我之前已经检查过了。同时,将0和32的差异视为“匹配”将意味着*(42)和J(74)相等:-),因此,即使在ASCII中,您的代码对于Laitn字母表中不存在的字符也会返回“Jump”和“*ump”的匹配。例如,对于“0”和“P”或“0”和“\x10”@53by97,它将返回true。请小心过早和幼稚的优化。我非常确定,无论您的代码做什么,尝试优化
equalsIgnoreCase
都不会带来任何显著的改进。@53by97:“但在几乎所有情况下,我们都不会经常遇到Unicode“-你是多么与世隔绝啊。在美国的某些地区可能是这样;但这在t中是完全不真实的