【Java必修课】判断String是否包含子串的四种方法及性能对比
source link: https://segmentfault.com/a/1190000021974831
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
1 简介
判断一个字符串是否包含某个特定子串是常见的场景,比如判断一篇文章是否包含敏感词汇、判断日志是否有 ERROR
信息等。本文将介绍四种方法并进行性能测试。
2 四种方法
2.1 JDK原生方法String.indexOf
在 String
的函数中,提供了 indexOf(subStr)
方法,返回子串 subStr
第一次出现的位置,如果不存在则返回-1。例子如下:
//包含Java assertEquals(7, "Pkslow Java".indexOf("Java")); //如果包含多个,返回第一次出现位置 assertEquals(0, "Java Java".indexOf("Java")); //大小写敏感 assertEquals(-1, "Google Guava".indexOf("guava"));
2.2 JDK原生方法String.contains
最直观判断的方法是 contains(subStr)
,返回类型为 boolean
,如果包含返回 true
,不包含则返回 false
。例子如下:
//包含Java assertTrue("code in Java".contains("Java")); //大小写敏感,不包含GO assertFalse("Let's go".contains("GO")); //转为大写后包含 assertTrue("Let's go".toUpperCase().contains("GO"));
实际上, String
的 contains
方法是通过调用 indexOf
方法来判断的,源码如下:
public boolean contains(CharSequence s) { return indexOf(s.toString()) > -1; }
2.3 JDK原生正则匹配Pattern
通过强大的正则匹配来判断,虽然有点杀鸡用牛刀的感觉,但也不是不能用,例子如下:
Pattern pattern = Pattern.compile("Java"); //包含Java Matcher matcher1 = pattern.matcher("Python, Java, Go, C++"); assertTrue(matcher1.find()); //不包含Java Matcher matcher2 = pattern.matcher("Python, C, Go, Matlab"); assertFalse(matcher2.find());
2.4 Apache库StringUtils.contains
Apache的 commons-lang3
提供许多开箱即用的功能, StringUtils
就提供了许多与字符串相关的功能,例子如下:
//包含sub assertTrue(StringUtils.contains("String subString", "sub")); //大小写敏感 assertFalse(StringUtils.contains("This is Java", "java")); //忽略大小写 assertTrue(StringUtils.containsIgnoreCase("This is Java", "java"));
3 性能对比
我们使用 JMH
工具来对四种方法进行性能测试, Maven
引入代码如下:
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>${openjdk.jmh.version}</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>${openjdk.jmh.version}</version> </dependency>
测试代码如下:
@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class StringContainsPerformanceTest { @State(Scope.Thread) public static class MyState { private String text = "If you want to be smart; read. If you want to be really smart; read a lot."; Pattern pattern = Pattern.compile("read"); } @Benchmark public int indexOf(MyState state) { return state.text.indexOf("read"); } @Benchmark public boolean contains(MyState state) { return state.text.contains("read"); } @Benchmark public boolean stringUtils(MyState state) { return StringUtils.contains(state.text, "read"); } @Benchmark public boolean pattern(MyState state) { return state.pattern.matcher(state.text).find(); } public static void main(String[] args) throws Exception { Options options = new OptionsBuilder() .include(StringContainsPerformanceTest.class.getSimpleName()) .threads(6) .forks(1) .warmupIterations(3) .measurementIterations(6) .shouldFailOnError(true) .shouldDoGC(true) .build(); new Runner(options).run(); } }
测试结果如下:
Benchmark Mode Cnt Score Error Units contains avgt 6 11.331 ± 1.435 ns/op indexOf avgt 6 11.250 ± 1.822 ns/op pattern avgt 6 101.196 ± 12.047 ns/op stringUtils avgt 6 29.046 ± 3.873 ns/op
最快的就是 indexOf
方法,其次是 contains
方法,二者应该没有实际区别, contains
是调用 indexOf
来实现的。Apache的 StringUtils
为第三方库,相对慢一些。最慢的是使用了正则的 Pattern
的方法,这不难理解,正则引擎的匹配是比较耗性能的。
4 总结
本文介绍了判断一个字符串是否包含某个特定子串的四种方法,并通过性能测试进行了对比。其中性能最好的是String的 indexOf
方法和 contains
方法,建议使用 contains
方法,性能好,跟 indexOf
相比,更直观,更不容易犯错。毕竟让每个人时刻记住返回 -1
代表不存在也不是一件容易的事。
但是,使用 indexOf
和 contains
方法都需要注意做判空处理,这时 StringUtils
的优势就体现出来了。
欢迎关注公众号< 南瓜慢说 >,将持续为你更新...
多读书,多分享;多写作,多整理。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK