Java equalsIgnoreCase()与toUpperCase().equals和toLowerCase().equals的效率

Java equalsIgnoreCase()与toUpperCase().equals和toLowerCase().equals的效率,java,string,performance,Java,String,Performance,标题说明了一切。我的问题是关于不同的String等价方法的效率。我经常使用.equalsIgnoreCase(String str),因为我对它很有兴趣。但是我开始怀疑它是否是在字符串之间寻找等价性的最有效方法。在我看来,.equalsIgnoreCase(String str)正在调用一种大小写转换方法toUpperCase或toLowerCase,然后在其定义中调用equals,但我可能错了。那么,以下哪种方法在以下情况或任何情况下更有效 int count = 0;//checks for

标题说明了一切。我的问题是关于不同的
String
等价方法的效率。我经常使用
.equalsIgnoreCase(String str)
,因为我对它很有兴趣。但是我开始怀疑它是否是在
字符串之间寻找等价性的最有效方法。在我看来,
.equalsIgnoreCase(String str)
正在调用一种大小写转换方法
toUpperCase
toLowerCase
,然后在其定义中调用
equals
,但我可能错了。那么,以下哪种方法在以下情况或任何情况下更有效

int count = 0;//checks for face cards at indexes listed in selectedCards
              // Selected cards is Integer ArrayList

    for(; (count < selectedCards.size() && count < 3); count++)
    {
        if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("Queen"))
            count++;
        else if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("King"))
            count++;
        if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("Jack"))
            count++;
    }

    if(count == 3)
        return true;
    return false;
int count=0//在selectedCards中列出的索引处检查人脸卡
//所选卡片为整数数组列表
对于(;(计数
您可以自己检查代码(它包含在SDK中)

.equalsIgnoreCase比ToPerCase()快。equals()比ToPerCase快 在java 8中,代码是:

   while (len-- > 0) {
        char c1 = ta[to++];
        char c2 = pa[po++];
        if (c1 == c2) {
            continue;
        }
        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;
            }
        }
        return false;
    }
    return true;
因此,如果比较的字符串实际上是不同的,它将比第一次调用toUpderCase然后调用equals更快,因为touppercase将修改所有字符。此外,toUperCase中涉及的逻辑似乎比比较循环中更复杂,并且它在末尾创建了一个新的String对象


但是您需要进行大量比较操作才能真正看到任何差异。

令人惊讶的是,由于一些奇怪的原因,这些方法并不等效:

  • toUpperCase(“ß”)
    返回
    “SS”
    ,即两个字母,而
    equalsIgnoreCase
    按字符工作
  • 有些字符的
    toUpperCase
    toLowerCase
    都不够,您必须同时执行这两项操作
关于效率,我敢打赌
equalsIgnoreCase
要快得多,因为它不会复制任何数据。它还从长度比较开始

还请注意,
toUpperCase
toLowerCase
Locale
敏感,而
equalsIgnoreCase
则不敏感。IIRC如果使用外来语言环境,性能会降低

您的用例 最佳和最简单的优化方法是事先对套管进行规范化处理。没有理由在你的数据中有一个“女王”、“女王”和一个“女王”——尽快清理输入

您还可以使用
enum
表示排名

恐怕你的线圈坏了。在任何一张JQK之后,你跳过了一张卡,这是有意的吗

简化代码 执行以下操作

  • 使用foreach循环保持其清洁
  • 使用局部变量避免重复复杂的表达式
  • 如果(x)返回true,则替换
    ;否则返回false
    返回x
不使用
enum
s和早期规范化的代码如下所示:

int count = 0;
// Whatever Position is
for (Position p : selectedCards) {
    String rank = cardAt(p).rank();
    if (rank.equalsIgnoreCase("Jack")
            || rank.equalsIgnoreCase("Queen")
            || rank.equalsIgnoreCase("King")) {
        ++count;
    if (count > 3) { // Tiny and probably useless optimization.
        return false;
    }
}
return count == 3;

JMH使微基准标记变得简单:

更新:将输入字符串设置为参数,以处理来自JMH God Alexey Shipilev的注释。我将目标字符串保留为常量,因为OP的用例是将输入字符串与常量进行比较

@State(Benchmark)
public class StrComp {

    @Param({"Queen", "queen", "King"})
    public String input;

    @Benchmark
    public boolean eqIgnoreCase() {
        return input.equalsIgnoreCase("queen");
    }

    @Benchmark
    public boolean eqToLower() {
        return input.toLowerCase().equals("queen");
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(".*StrComp.*")
                .mode(Mode.AverageTime)
                .timeUnit(TimeUnit.NANOSECONDS)
                .forks(5)
                .warmupIterations(10)
                .measurementIterations(10)
                .build();

        new Runner(opt).run();
    }
}
以及输出:

Benchmark             Mode  Cnt   Score   Error  Units
StrComp.eqIgnoreCase  avgt   50  18.581 ± 0.051  ns/op
StrComp.eqToLower     avgt   50  54.796 ± 0.173  ns/op
使用参数更新输出:

Benchmark             (input)  Mode  Cnt   Score   Error  Units
StrComp.eqIgnoreCase    Queen  avgt   50  17.947 ± 0.205  ns/op
StrComp.eqIgnoreCase    queen  avgt   50  15.553 ± 0.159  ns/op
StrComp.eqIgnoreCase     King  avgt   50   2.968 ± 0.037  ns/op
StrComp.eqToLower       Queen  avgt   50  56.499 ± 0.180  ns/op
StrComp.eqToLower       queen  avgt   50  22.023 ± 0.040  ns/op
StrComp.eqToLower        King  avgt   50  49.174 ± 0.145  ns/op
因此,eqIgnoreCase速度更快,但除非每秒进行一百万次比较,否则不会注意到任何差异

如果第一个字符串已经是小写的,或者字符串的长度不同,等等,你可以到处玩,看看差异会受到什么影响

无论如何,如果你想让你的代码更“高效”,更清晰,更安全,更不容易出现错误,就不要使用字符串。使用枚举


卡片组非常适合使用enum实现,它经常被用来说明
enum
概念:

您自己尝试过对它们进行基准测试吗?@dr_andonuts我不知道如何做。如果可以,我会的。我只是好奇。我的意思是,在一个循环中运行每一个一百万次,然后比较结果。@dr_andonuts我以前出于好奇检查过效率问题,他们总是有这些非常奇怪的变量、数字和方法等等。如果我自己试一试的话,我会测量什么?不,你不能移动if语句。“…事先使外壳正常化。”--这是正确的答案。谢谢你发现了回路的问题。我没有注意到我是如何编写for循环的。天啊,我有时候真是太蠢了。我很感激你的回答,本来会把它标记为答案的,但是米莎展示了基准,所以我和他一起去了。@JohnnyCoder我很满意。Java的基准测试非常重要,他做得很好。也许有一天我需要在几秒钟内比较几十亿个字符串。谢谢你,米莎。老实说,我不明白你对主要方法做了什么,但我一直想了解基准测试,所以我想我现在有理由学习它。不过,我确实理解结果。非常感谢。主要方法是告诉JMH框架如何运行基准测试。这些只是可以提供的一些配置参数。JMH太棒了。看看吧,很容易做对,也很容易出错。至少,输入不应该是恒定的(参见)。而且,您需要测试相同/不同的长度以获得完整的图像。另外,从CLI运行此基准测试不需要使用
main
方法。@AlekseyShipilev我尝试过使用非常量字符串并得到了结果