Java 字符串查找性能对比:indexOf 与 KMP/BF 算法实测

慢慢来
慢慢来
发布时间:2026-10-14| 4人浏览过
原创

本文通过具体代码实例,验证了 Java 中 String 内置方法 indexOf 与传统 BF 及 KMP 算法在处理大规模文本匹配时的性能差异,分析了StringBuilder生成测试数据的开销影响。

背景与测试目标

在进行算法研究(如 KMP 算法)时,为了量化评估执行时间和性能表现,需要搭建基准测试环境。常见的做法是在内存中构造海量随机字符串,对比不同查找算法的效率。

测试原理与代码实现

本次测试主要围绕以下三个核心维度展开: 1. JDK 原生方法:直接调用 String.indexOf()。 2. BF 算法 (Brute Force):经典的暴力匹配逻辑,包含回溯操作。 3. KMP 算法:利用 next 数组减少回溯次数的高效匹配方案。

完整的测试辅助类及核心逻辑代码如下:

package com.test.test.kmp;

import java.util.Random;

public class KMPTest {

	public static void main(String[] args) {
		test();
	}
	
	public static void test() {
		// 定义测试参数:主串长度800万,子串长度11,字符集类型4(实际代码为数字+大小写混合)
		int allLen = 8000000;
		int subLen = 11;
		int charLen = 4;
		String cl = buildString(charLen);
		int times = 50;
		// 执行匹配测试
		testMatch(allLen, subLen, cl, times);
	}
	
	public static void testMatch(int allLen, int subLen, String cl, int times){
		// 记录各项耗时累计
		int allBF = 0;
		int allString = 0;
		int allKMP = 0;
		int allGC = 0; // 垃圾回收带来的系统暂停时间
		int allbuild = 0; // 字符串构建耗时
		int alltoArray = 0; // toCharArray转换耗时
		
		long start = System.currentTimeMillis();
		System.out.println("--------------------------");
		
		for (int i = 0; i < times; i++) {
			// 1. 消耗测试:字符串构建
			long buildStart = System.currentTimeMillis();
			String sub = buildString(subLen, cl);
			String all = buildString(allLen, sub) +sub;
			long buildEnd = System.currentTimeMillis();
			allbuild += (buildEnd - buildStart);
			
			// 2. 消耗测试:转换为 char 数组
			long toArrayStart = System.currentTimeMillis();
			char[] allArray = all.toCharArray();
			char[] subArray = sub.toCharArray();
			long toArrayEnd = System.currentTimeMillis();
			alltoArray += (toArrayEnd - toArrayStart);
			
			// --- 执行 BF 算法 ---
			long startBF = System.currentTimeMillis();
			int indexBF = bfIndexOf(all, sub);
			long endBF = System.currentTimeMillis();
			int timeoutBF = (int)(endBF - startBF);
			allBF += timeoutBF;
			System.gc();
			allGC += (int)(System.currentTimeMillis() - endBF);
	
			// --- 执行 JDK indexOf 方法 ---
			long startString = System.currentTimeMillis();
			int indexString = stringIndexOf(all, sub);
			long endString = System.currentTimeMillis();
			int timeoutString = (int)(endString - startString);
			allString += timeoutString;
			System.gc();
			allGC += (int)(System.currentTimeMillis() - endString);
			
			// --- 执行 KMP 算法 ---
			long startKMP = System.currentTimeMillis();
			int indexKMP = KMP_Index(allArray, subArray);
			long endKMP = System.currentTimeMillis();
			int timeoutKMP = (int)(endKMP - startKMP);
			allKMP += timeoutKMP;
			System.gc();
			allGC += (int)(System.currentTimeMillis() - endKMP);
			
			// 结果校验
			if(indexBF == indexString && indexKMP == indexString){
				// 一致继续
			} else {
				throw new RuntimeException("对比出错.");
			}
			
			// 阶段性输出中间状态
			if(i % 20 == 10){
				printLog(i, allBF, allString, allKMP, allbuild, alltoArray, allGC, start);
			}
		}
		
		// 最终汇总输出
		printFinalLog(times, allBF, allString, allKMP, allbuild, alltoArray, allGC, start);
	}
	
	// 辅助打印方法(略)
	
	public static String buildString(int len, String sub){
		final int TYPE_NUMBER = 0;
		final int TYPE_LOWERCASE = 1;
		final int TYPE_UPPERCASE = 2;
		long seed = System.nanoTime();
		Random random = new Random(seed);
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < len; i++) {
			int type = random.nextInt(3);
			int cvalue = 0;
			if(TYPE_NUMBER == type){
				cvalue = '0' + random.nextInt(10);
			} else if(TYPE_LOWERCASE == type){
				cvalue = 'a' + random.nextInt(26);
			} else if(TYPE_UPPERCASE == type){
				cvalue = 'A' + random.nextInt(26);
			}
			char c = (char)cvalue;
			if(null != sub && sub.length() > 1){
				int index = random.nextInt(sub.length());
				c = sub.charAt(index);
			}
			builder.append(c);
		}
		return builder.toString();
	}
	
	public static int stringIndexOf(String all, String sub){
		if(null == all || null== sub) return -1;
		return all.indexOf(sub);
	}
	
	public static int bfIndexOf(String all, String sub){
		if(null == all || null== sub) return -1;
		int lenAll = all.length();
		int lenSub = sub.length();
		int i = 0;
		while (i < lenAll) {
			int j = 0;
			while (j < lenSub) {
				if(all.charAt(i) == sub.charAt(j)){
					i++;
					j++;
				} else {
					break;
				}
			}
			if(lenSub == j) return i - lenSub;
			else i = 1+i - j;
		}
		return -1;
	}
	
	public static int KMP_Index(char[] allArray, char[] subArray) { 
		int[] next = next(subArray); 
		int i = 0; 
		int j = 0; 
		while (i <= allArray.length - 1 && j <= subArray.length - 1) { 
			if (j == -1 || allArray[i] == subArray[j]) { 
				i++; 
				j++; 
			} else { 
				j = next[j]; 
			} 
		}
		if (j < subArray.length) return -1;
		else return i - subArray.length;
	}
	
	public static int[] next(char[] t) { 
		int[] next = new int[t.length]; 
		next[0] = -1; 
		int i = 0; 
		int j = -1; 
		while (i < t.length - 1) { 
			if (j == -1 || t[i] == t[j]) { 
				i++; 
				j++; 
				if (t[i] != t[j]) next[i] = j;
				else next[i] = next[j]; 
			} else { 
				j = next[j]; 
			} 
		}
		return next; 
	}
}

测试结果与分析

在设置主串长度为 800 万、子串长度为 11 的情况下,运行 50 次循环,获取的关键性能数据如下:

--------------------------
10次bfIndexOf= 94ms
10次stringIndexOf= 56ms
10次KMPIndexOf= 76ms
... 
50次bfIndexOf;总计耗时: 550ms
50次stringIndexOf;总计耗时: 335ms
50次KMPIndexOf;总计耗时: 450ms
...
  • JDK 原生 indexOf 最快:平均耗时约 335ms。SUN/Oracle 在其标准库实现中进行了深度底层优化,往往比常规算法实现更加高效。
  • KMP 算法位列第二:约为 450ms。尽管在理论复杂度上有优势,但由于引入了 next 数组计算及额外的索引操作,在某些短子串场景下可能并未展现出碾压优势。
  • BF 算法相对最慢:约为 550ms。频繁的回溯操作导致了更多的 CPU 周期消耗。

额外发现:字符串构建的隐形成本

除了核心算法对比,测试代码中还暴露了一个重要的性能陷阱:StringBuilder 的数据准备耗时占据了总时间的绝大部分。

根据日志显示,在 50 次循环中:

  • 字符串构建耗时 (allbuild):高达 26,501ms。
  • 核心算法总耗时 (BF/String/KMP):仅占几百毫秒级别。

这意味着使用 StringBuilder 追加拼接 800 万个随机字符是一个非常昂贵的操作(换算约每秒 1600 万次 append)。在进行此类极大规模的微基准测试时,如果不单独剥离“数据准备”这一前置步骤,会导致测试结论失真。

改进建议

  1. 数据预热**:如果必须测试这种规模的算法,建议先将生成的超大字符串写入本地文件,并在测试前读入内存,避免每次循环都重复创建对象。
  2. 关注 GC 影响:如此大对象的频繁创建会触发 System.gc() 及堆内存压力,测试时应留意 Garbage Collection 造成的毫秒级卡顿。
  3. 工具选择**:对于简单的统计场景,JDK 原生方法通常是最优解;仅在特定数据结构或受限环境下,才有必要手动实现 KMP。

以上就是Java中的字符串查找性能测试的详细分析内容,更多关于Java基础算法优化与性能调优的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。