【问题标题】:Very fast search for specific characters in Java在 Java 中快速搜索特定字符
【发布时间】:2013-02-20 02:28:11
【问题描述】:

这似乎是一个有点愚蠢的问题。也许它是。但我有一个我经常使用的功能,想知道这是否是完成这项工作的最快方法。该功能被使用了很多次,以至于任何速度的提高实际上都是显而易见的。它所做的只是检查一个字符是否是一个核苷酸(即:如果一个字符是 'A'、'T'、'C' 或 'G'。

private static boolean isValidNucleotide(char nucleotide) {
    nucleotide = Character.toUpperCase(nucleotide);
    if(nucleotide == 'A') return true; 
    if(nucleotide == 'T') return true;
    if(nucleotide == 'C') return true;
    if(nucleotide == 'G') return true;
    return false;
}

这是完成工作的最快方法吗?还是您认为值得实现某种索引/映射/其他东西(可能在函数之外执行比较,然后将此文本复制到代码中的几个位置)?我真的不是 Java 中这类事情的专家。

【问题讨论】:

  • 退后一步,重新审视实际的问题,而不是你的解决方案。实际上,以完全不同的方式执行此操作可能会更快,例如,预先限制用户输入,或尽早检查整个核苷酸序列,以便以后无需重复检查。甚至可能有理由(如果您更关心速度而不是有效性)根本不检查。
  • @paxdiablo 感谢您的建议。我确实接受了答案,但这些 cmets 绝对值得考虑。我确实必须验证,但可能有一种方法可以限制链中较早的输入。

标签: java performance search character


【解决方案1】:

最快(但内存效率最低,仍然 255 字节还不错!)会是这样的:

/* this is static member of class */
static boolean map[] = new boolean[256];
static {
    for(int j = 0; j < map.length; j++)
        map[j] = false;
    /* map your required values true here */ 
    map['A'] = true;
    map['T'] = true;
    map['C'] = true;
    map['G'] = true;
    /* make small letter here too */
    map['a'] = true;
    map['t'] = true;
    map['c'] = true;
    map['g'] = true;
}

然后做一个这样的函数:

private static boolean isValidNucleotide(char nucleotide) {
    /* complexity is just one access to array */
    return map[nucleotide];
}

正如@paxdiablo 所说,在 java 中 char 是 2 个字节而不是 1 个字节,但你的字符在这个范围内。只需将return map[nucleotide]; 更改为return map[0x00ff &amp; nucleotide]; 即可。

您还可以将地图大小更改为65536,以确保安全并避免任何类型的错误。 boolean map = new boolean[65536]

【讨论】:

  • 如果字符高于 255会怎样? Java 本身不是 Unicode 吗?
  • 好像是这样。我做了一个类似于 iTech 所做的测试。令人印象深刻!
  • 顺便说一句,您还应该将其更改为 256,从而为您提供索引 0..255。
  • 所以事实上,如果核苷酸高于 255 会发生什么情况,那就是你得到了一个异常!或者如果你掩盖它,你可能会得到错误的答案......这更糟。
  • @paxdiablo 谢谢!是的,有道理。
【解决方案2】:

你可以试试switch-case,它通常实现为小开关的表查找:

switch(nucleotide) {
case 'A':
case 'T':
case 'C':
case 'G':
    return true;
}
return false;

请注意,如果调用足够频繁,JVM 的 JIT 可能会使基于 if 的代码非常快。

【讨论】:

  • 有趣,是的,我想知道 JVM 是否会自行进行这些优化。不过,这看起来很可靠。我会尝试一个测试,看看它对运行时有什么影响。
  • 您也可以尝试通过将小写字母添加到switch 来替换toUpperCase 调用。
  • 检查后,接受的答案比我机器上的这个更快。不过,这个答案也很好,因为它也很快并且意图更清晰,这意味着它可能会从 JRE 的未来优化中获得更多好处。
【解决方案3】:

摆脱Character.toUpperCase 并检查大写和小写,这将显着加快您的功能。

private static boolean isValidNucleotide(char nucleotide) {       
    if(nucleotide == 'A' || nucleotide == 'a') return true; 
    // Rest of your conditions

    return false;
}

我用你的原始函数做了一个小测试,平均 80 ms 执行10000000 times 但是当我删除 Character.toUpperCase() 并明确检查这两种情况时,它只需要 40 ms,这是显着的改进。

编辑:

使用@Shivam Kalra 建议的Map 解决方案平均只需要11 ms

【讨论】:

  • 尝试将switch (nucleotide)更改为switch ((byte) nucleotide)
  • 我试过了,但不影响性能,还用了40 ms
  • 地图解决方案更快,因为它跳过了一些范围检查。仅当您可以假设输入字符都小于 256 时,它才有效。
  • (我的想法是,也许可以说服 JIT 编译器使用更大的开关表并避免一些“不必要的”范围测试。这样会更快。)
【解决方案4】:

我没试过,不过你可以试试用Regex看看效果

【讨论】:

    猜你喜欢
    • 2013-01-20
    • 2014-09-19
    • 2013-01-06
    • 2011-03-01
    • 1970-01-01
    • 2016-08-23
    • 2012-01-22
    • 2013-11-10
    • 1970-01-01
    相关资源
    最近更新 更多