欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Java必修课】判断String是否包含子串的四种方法及性能对比

程序员文章站 2022-09-04 12:12:22
1 简介 判断一个字符串是否包含某个特定子串是常见的场景,比如判断一篇文章是否包含敏感词汇、判断日志是否有 信息等。本文将介绍四种方法并进行性能测试。 2 四种方法 2.1 JDK原生方法String.indexOf 在 的函数中,提供了 方法,返回子串 第一次出现的位置,如果不存在则返回 1。例子 ......

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

实际上,stringcontains方法是通过调用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代表不存在也不是一件容易的事。

但是,使用indexofcontains方法都需要注意做判空处理,这时stringutils的优势就体现出来了。


欢迎关注公众号<南瓜慢说>,将持续为你更新...

【Java必修课】判断String是否包含子串的四种方法及性能对比

多读书,多分享;多写作,多整理。