为什么String.equalsIgnoreCase太慢了

53b*_*y97 21 java string performance ignore-case

我在面试中遇到一个问题,即写一个方法来检查类似的单词而不考虑字符情况.

我通过使用每对字符的ASCII值的差异来回答它.但是在家里,当我在String.class中实际执行它时,我感到很不安 - 为什么这样实现呢!

我试图在inbuilt和我的自定义方法之间进行比较,这种方式 -

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)));
    }

}
Run Code Online (Sandbox Code Playgroud)

输出 -

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

使用custom()的平均毫秒数 - 5

我发现内置方法的效率很高,因为有很多检查和方法调用.这种实施背后有什么具体原因吗?或者我在逻辑中遗漏了什么?

任何建议,将衷心感谢!

Pau*_*eau 63

您的例程只处理ASCII字符.系统一个处理所有unicode字符.

考虑以下示例:

public class Test {

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

}
Run Code Online (Sandbox Code Playgroud)

  • @ 53by97:"但在几乎所有情况下,我们都没有经常遇到Unicode" - 多么令人愉快的孤立.在美国部分地区可能会出现这种情况; 但这在全世界都是完全不真实的.您的玩具实现可能适合您所需的特定狭窄领域,但图书馆设计人员需要更强大地构建其代码. (40认同)
  • @ 53by97正如我的一位教授曾经说过的"如果它不必是正确的,我可以任意快速地".而且真的任何人都认为英语只包含ASCII字符就足以让人惊讶;) (32认同)
  • @ 53by97小心过早和天真的优化.我很确定无论你的代码做什么,尝试优化`equalsIgnoreCase`都不会增加任何重大改进. (26认同)
  • 还有一个假的快捷方式,`if(str2.length()!= length)`.`"weißbier".length()!="WEISSBIER".length()`. (12认同)
  • 甚至英语也有许多带有变音符号的字符,例如上面的天真,或者简历,曝光...... (7认同)
  • 编写所有String以便处理Unicode字符.ASCII只是一个小集合,这使得只检查ASCII字符变得容易.如果你看一下toUpperCase/toLowerCase等的内部实现,那么我们可以想象的复杂性要多得多...... :) (4认同)
  • @JackAidley:53by97实际上是印度人.ASCII沙文主义在我国很普遍.当我们想用我们的母语编写时,我们将它们转换为ASCII而不是使用正确的脚本.Unicode的?为什么有人需要Unicode?/秒 (4认同)
  • 系统可以更快吗?大概.代码速度,开发人员时间和代码可读性之间存在权衡.如果最大速度对您的用例很重要,那么请务必使用您的例程(先修复它!:).但是,您应该仔细考虑是否需要它.例如,如果您将来需要处理西班牙语名称并因为几年前的优化而中断了所有内容,会发生什么? (3认同)
  • @AnubhavChattoraj:我为承认美国身份而道歉.尽管如此,更广泛的观点仍然存在. (3认同)
  • @ 53by97你的第一个陈述是错误的.大多数使用拉丁字符或西里尔字符的语言都具有ASCII范围之外的特殊或重音字符,您需要正确处理它们的情况 (2认同)
  • @LưuVĩnhPhúc:使用多部分测试仍然有很大的价值:首先测试参考相等,然后是序数相等,然后是使用基于ASCII的大小写折叠的快速相等/不等式(两个不匹配的ASCII字符应该尽早-exit`false`),然后最后做一个更复杂的测试.虽然有许多字符串组合,其相等性只能通过慢速检查过程来确定,但在许多使用情况下,这种测试通常是可以避免的. (2认同)
  • 另外:土耳其语中的`\ u0069`('i') - >`\ u0130`(不是'我')和'\ u0131` - >`\ u0049`('我') (2认同)

mer*_*ike 56

您的方法在许多方面都不正确.例如,它考虑"!" 等于"B","B"等于"1",但"!" 不等于"1"(所以它不是传递的,因为我们期望等于方法).

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

您可能还希望了解如何在Java中编写正确的微基准测试?获得更可靠的性能测量.

  • +1 - 让我想起曾经读过的一个讨论:"a:啊,但你的方法是我的慢两倍"."b:如果我被允许返回不正确的结果,我可以让我的方法在恒定的时间内运行" (25认同)

Nic*_*udo 11

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

有一些(恼人的)语言环境,其中两个字符可能具有相同的大写但不是相同的小写字母.出于这个原因,为了工作(大多数情况下,请参阅土耳其语),规范实现必须比较字符串char-for-char的大小写.

您的实现可能在99%的时间内完美,特别是如果您只需要处理英语区域设置,但遗憾的是核心库实现不会做出这样的假设.

  • 对不起,我的意思是`equalsToIgnoreCase`无法用于某些特定的语言环境(例如土耳其语,因为'i`问题),而不是因为小写/大写问题 - 虽然我可以看到这不清楚在我说出我的句子的方式.谢谢你提供了一个问题的实际例子:我在理智上知道这个问题,但之前从未见过*. (2认同)