Java 在字符串中查找子字符串的最佳方法

Java 在字符串中查找子字符串的最佳方法,java,ruby,string,Java,Ruby,String,我有一个问题,我试图搜索字符串中的子字符串。该子字符串可能在字符串中,也可能不在字符串中 str = "hello how are you?" substr = "how are" 我知道有两种方法可以做到: string.indexOf(“你好”) 正则表达式 但是,还有其他的“优化”方式吗?你会怎么做 Ruby能提供更好的答案吗?因为我们使用jRuby,所以答案可以是Ruby或Java。最好的方法是indexof,regex在Ruby中比较慢,使用字符串#include?方法: str =

我有一个问题,我试图搜索字符串中的子字符串。该子字符串可能在字符串中,也可能不在字符串中

str = "hello how are you?"
substr = "how are"
我知道有两种方法可以做到:

  • string.indexOf(“你好”)
  • 正则表达式
  • 但是,还有其他的“优化”方式吗?你会怎么做


    Ruby能提供更好的答案吗?因为我们使用jRuby,所以答案可以是Ruby或Java。

    最好的方法是indexof,regex在Ruby中比较慢

    ,使用
    字符串#include?
    方法:

    str = "hello how are you?"
    substr = "how are"
    str.include? substr 
    

    它返回
    true

    据我所知,没有“神奇”的方法可以快速搜索子字符串,除非您事先准备好构建某种搜索元数据(想想索引)。除非经常搜索同一字符串,否则这样做很可能会浪费比节省更多的时间


    由于搜索模式很简单,我会避开正则表达式。

    如果只想检查子字符串是否在字符串中,可以使用:
    str[substr]

    它返回substring或nil。

    有关“其他方式”的概述,您可以从Wikipedia上的“”文章开始


    如Martin所述,使用“”对字符串进行索引是一种非常明显的加快速度的方法,这仅适用于对同一字符串进行多次搜索的情况:

    如果您确信运行时系统中的功能(提供字符串搜索等)对于您的应用程序来说不够快,尝试实现算法

    但是,现代运行时系统的实现者可能已经为您完成了这项工作

    “你会怎么做?”

    我会做一个基准测试,试着比较完成同一件事的不同方法,以了解哪种方法最快

    在Ruby的旧版本中,我们会看到基于正则表达式的搜索运行得更慢。我将1.9.2中的新引擎用于基准测试,它带来了很大的不同。特别是,过去,非固定搜索比固定搜索慢得多。现在,无论是使用正则表达式还是固定字符串搜索大部分内容,都是一个大问题。在不预编译正则表达式的情况下使用match()对速度来说是一个痛苦的打击,因此,如果使用相同的模式执行许多循环,那么将该模式分配给变量并引用该变量是有意义的

    显示的时间是每个测试执行“n”(750000)次迭代所需的时间,因此数字越小越好

    require 'benchmark'
    
    LOREM = %q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut et convallis purus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras interdum nibh et nunc pellentesque vestibulum. Donec elementum felis malesuada urna vehicula consectetur commodo urna accumsan. Phasellus bibendum euismod tincidunt. Sed pellentesque cursus faucibus. Etiam bibendum tincidunt nibh eget ultrices. Fusce imperdiet, felis id consequat imperdiet, justo est ultrices elit, sed vestibulum dui nibh vel felis. Sed feugiat, libero quis consequat semper, magna tellus facilisis enim, rutrum adipiscing eros mauris commodo metus. Sed lobortis aliquet augue ac sodales. Quisque pharetra odio vel augue tempus porttitor.}
    
    REGEX1 = %r{/porttitor\.$/}
    REGEX2 = %r{/porttitor\./}
    REGEX3 = %r{/porttitor\.\Z/}
    
    n = 750_000
    puts "word in string"
    Benchmark.bm(15) do |x|
      x.report('string[""]:')   { n.times { LOREM['porttitor.']          } }
      x.report('string[//]:')   { n.times { LOREM[/porttitor\./]         } } # unanchored regex
      x.report('string[/$/]:')  { n.times { LOREM[/porttitor\.$/]        } } # anchored regex
      x.report('string[/\Z/]:') { n.times { LOREM[/porttitor\.\Z/]       } } # anchored regex
      x.report('index():')      { n.times { LOREM.index('porttitor.')    } }
      x.report('include?():')   { n.times { LOREM.include?('porttitor.') } }
      x.report('match($):')     { n.times { LOREM.match(/porttitor\.$/)  } }
      x.report('match(\Z):')    { n.times { LOREM.match(/porttitor\.\Z/) } }
      x.report('match():')      { n.times { LOREM.match(/porttitor\./)   } }
      x.report('match2($):')    { n.times { LOREM.match(REGEX1)          } } # compiled regex w/ anchor
      x.report('match2():')     { n.times { LOREM.match(REGEX2)          } } # compiled report w/out anchor
      x.report('match2(\Z):')   { n.times { LOREM.match(REGEX3)          } } # compiled regex w/ anchor
    end
    
    puts
    puts "word not in string"
    Benchmark.bm(15) do |x|
      x.report('string[""]:')   { n.times { LOREM['porttit0r.']          } }
      x.report('string[//]:')   { n.times { LOREM[/porttit0r\./]         } } # unanchored regex
      x.report('string[/$/]:')  { n.times { LOREM[/porttit0r\.$/]        } } # anchored regex
      x.report('string[/\Z/]:') { n.times { LOREM[/porttit0r\.\Z/]       } } # anchored regex
      x.report('index():')      { n.times { LOREM.index('porttit0r.')    } }
      x.report('include?():')   { n.times { LOREM.include?('porttit0r.') } }
      x.report('match($):')     { n.times { LOREM.match(/porttit0r\.$/)  } }
      x.report('match(\Z):')    { n.times { LOREM.match(/porttit0r\.\Z/) } }
      x.report('match():')      { n.times { LOREM.match(/porttit0r\./)   } }
    end
    
    对于输出:

    word in string
                          user     system      total        real
    string[""]:       0.670000   0.000000   0.670000 (  0.675319)
    string[//]:       0.700000   0.000000   0.700000 (  0.706148)
    string[/$/]:      0.720000   0.000000   0.720000 (  0.716853)
    string[/\Z/]:     0.530000   0.000000   0.530000 (  0.527568)
    index():          0.630000   0.000000   0.630000 (  0.638562)
    include?():       0.610000   0.000000   0.610000 (  0.603223)
    match($):         1.690000   0.000000   1.690000 (  1.696045)
    match(\Z):        1.520000   0.010000   1.530000 (  1.532107)
    match():          1.700000   0.000000   1.700000 (  1.698748)
    match2($):        0.840000   0.000000   0.840000 (  0.847590)
    match2():         0.840000   0.000000   0.840000 (  0.840969)
    match2(\Z):       0.840000   0.000000   0.840000 (  0.835557)
    
    word not in string
                          user     system      total        real
    string[""]:       0.570000   0.000000   0.570000 (  0.578120)
    string[//]:       0.740000   0.000000   0.740000 (  0.734751)
    string[/$/]:      0.730000   0.000000   0.730000 (  0.735599)
    string[/\Z/]:     0.560000   0.000000   0.560000 (  0.563673)
    index():          0.620000   0.000000   0.620000 (  0.619451)
    include?():       0.570000   0.000000   0.570000 (  0.574413)
    match($):         0.910000   0.010000   0.920000 (  0.910059)
    match(\Z):        0.730000   0.000000   0.730000 (  0.726533)
    match():          0.950000   0.000000   0.950000 (  0.960865)
    
    以下是一些使用Ruby 1.8.7的数字,这是雪豹的默认值,仅供参考:

    word in string
                         user     system      total        real
    string[""]:      1.130000   0.000000   1.130000 (  1.130687)
    string[//]:      1.170000   0.000000   1.170000 (  1.165692)
    string[/$/]:     1.180000   0.000000   1.180000 (  1.184954)
    string[/\Z/]:    1.180000   0.000000   1.180000 (  1.179168)
    index():         1.070000   0.000000   1.070000 (  1.077791)
    include?():      1.060000   0.000000   1.060000 (  1.056430)
    match($):        1.470000   0.010000   1.480000 (  1.472797)
    match(\Z):       1.480000   0.000000   1.480000 (  1.490172)
    match():         1.480000   0.000000   1.480000 (  1.478146)
    match2($):       0.650000   0.000000   0.650000 (  0.653029)
    match2():        0.570000   0.000000   0.570000 (  0.574384)
    match2(\Z):      0.640000   0.000000   0.640000 (  0.646688)
    
    word not in string
                         user     system      total        real
    string[""]:      1.040000   0.000000   1.040000 (  1.038885)
    string[//]:      0.510000   0.000000   0.510000 (  0.507031)
    string[/$/]:     0.510000   0.000000   0.510000 (  0.508425)
    string[/\Z/]:    0.500000   0.000000   0.500000 (  0.507316)
    index():         1.060000   0.000000   1.060000 (  1.055157)
    include?():      1.030000   0.000000   1.030000 (  1.037060)
    match($):        0.630000   0.000000   0.630000 (  0.623627)
    match(\Z):       0.620000   0.000000   0.620000 (  0.624737)
    match():         0.620000   0.000000   0.620000 (  0.623049)
    

    我添加了额外的测试,以了解仅使用未编排和锚定正则表达式的效果:

    require 'fruity'
    
    LOREM = %{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut et convallis purus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras interdum nibh et nunc pellentesque vestibulum. Donec elementum felis malesuada urna vehicula consectetur commodo urna accumsan. Phasellus bibendum euismod tincidunt. Sed pellentesque cursus faucibus. Etiam bibendum tincidunt nibh eget ultrices. Fusce imperdiet, felis id consequat imperdiet, justo est ultrices elit, sed vestibulum dui nibh vel felis. Sed feugiat, libero quis consequat semper, magna tellus facilisis enim, rutrum adipiscing eros mauris commodo metus. Sed lobortis aliquet augue ac sodales. Quisque pharetra odio vel augue tempus porttitor.}
    
    compare do
      str_slice_regex  { LOREM[/porttitor\./]         } # unanchored regex
      str_slice_dollar { LOREM[/porttitor\.$/]        } # anchored regex
      str_slice_ctrlZ  { LOREM[/porttitor\.\Z/]       } # anchored regex
      str_slice_ctrlz  { LOREM[/porttitor\.\z/]       } # anchored regex
    end
    
    # >> Running each test 8192 times. Test will take about 1 second.
    # >> str_slice_ctrlz is similar to str_slice_ctrlZ
    # >> str_slice_ctrlZ is faster than str_slice_regex by 2x ± 0.1
    # >> str_slice_regex is similar to str_slice_dollar
    
    这是使用了Fruity,因此结果与上面的信息没有直接关联,但它仍然有用


    以下是一些最新信息:

    # >> Running on Ruby v.2.7.0
    # >> word in string
    # >>                       user     system      total        real
    # >> string[""]:       0.368283   0.000147   0.368430 (  0.368468)
    # >> string[//]:       0.329253   0.000080   0.329333 (  0.329466)
    # >> string[/$/]:      0.330270   0.000172   0.330442 (  0.330594)
    # >> string[/\Z/]:     0.183119   0.000048   0.183167 (  0.183209)
    # >> index():          0.358397   0.000289   0.358686 (  0.360185)
    # >> include?():       0.352700   0.000196   0.352896 (  0.353056)
    # >> match($):         0.761605   0.001502   0.763107 (  0.763297)
    # >> match(\Z):        0.631132   0.000507   0.631639 (  0.631767)
    # >> match():          0.765219   0.000634   0.765853 (  0.766199)
    # >> match2($):        0.394938   0.000128   0.395066 (  0.395173)
    # >> match2():         0.391687   0.000080   0.391767 (  0.391879)
    # >> match2(\Z):       0.389440   0.000089   0.389529 (  0.389678)
    # >> 
    # >> word not in string
    # >>                       user     system      total        real
    # >> string[""]:       0.365097   0.000117   0.365214 (  0.365262)
    # >> string[//]:       0.388117   0.000361   0.388478 (  0.389008)
    # >> string[/$/]:      0.381933   0.000091   0.382024 (  0.382061)
    # >> string[/\Z/]:     0.236101   0.000086   0.236187 (  0.236307)
    # >> index():          0.369898   0.000131   0.370029 (  0.370267)
    # >> include?():       0.361057   0.000065   0.361122 (  0.361202)
    # >> match($):         0.409296   0.000390   0.409686 (  0.410103)
    # >> match(\Z):        0.258903   0.000074   0.258977 (  0.259095)
    # >> match():          0.399220   0.000070   0.399290 (  0.399386)
    # >> --------------------
    # >> Running each test 16384 times. Test will take about 1 second.
    # >> str_slice_ctrlz is similar to str_slice_ctrlZ
    # >> str_slice_ctrlZ is faster than str_slice_dollar by 2x ± 0.1
    # >> str_slice_dollar is similar to str_slice_regex
    

    “”是相关的。

    干得好!但对我来说仍然很奇怪,str[substr]比regexp慢。理论上,str[substr]是实现良好性能的最简单方法。我还没有看源代码,但我怀疑str[substr]真的把括号内的每个子字符串都当作正则表达式,强制重新编译模式,或者跳转到其他执行固定字符串搜索的代码。在C/C++中执行固定字符串搜索很容易,因此必须进行其他处理。或者,我把基准做错了,但我不这么认为。我知道它很古老:但“最佳方式”在很大程度上取决于目标是什么。如果是易于开发的,
    str.include?(substring)
    非常好,但是如果是速度,那么可能还有其他更好的方法。为您的断言显示数据。我知道我来晚了,但请记住,“include?”是区分大小写的。