From 3f15fdd44c05710ce016b3472aab6c2ef888e88e Mon Sep 17 00:00:00 2001 From: LettuceLeaves <2878506229@qq.com> Date: Wed, 19 Nov 2025 01:03:42 +0800 Subject: [PATCH] =?UTF-8?q?perf:=E4=BD=BF=E7=94=A8Sunday=E7=AE=97=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E5=AD=97=E4=B8=B2=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/v7/core/text/CharSequenceUtil.java | 7 +- .../hutool/v7/core/text/finder/StrFinder.java | 95 +++++++++++++++++-- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/v7/core/text/CharSequenceUtil.java index ecf0fcdfb..a72f101c4 100644 --- a/hutool-core/src/main/java/cn/hutool/v7/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/v7/core/text/CharSequenceUtil.java @@ -2432,14 +2432,11 @@ public class CharSequenceUtil extends StrValidator { continue; } if (ignoreCase) { - final char u1 = Character.toUpperCase(c1); - final char u2 = Character.toUpperCase(c2); + final char u1 = Character.toLowerCase(c1); + final char u2 = Character.toLowerCase(c2); if (u1 == u2) { continue; } - if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { - continue; - } } return false; } diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/text/finder/StrFinder.java b/hutool-core/src/main/java/cn/hutool/v7/core/text/finder/StrFinder.java index 7c89619e3..f19b3a17d 100644 --- a/hutool-core/src/main/java/cn/hutool/v7/core/text/finder/StrFinder.java +++ b/hutool-core/src/main/java/cn/hutool/v7/core/text/finder/StrFinder.java @@ -20,6 +20,10 @@ import cn.hutool.v7.core.lang.Assert; import cn.hutool.v7.core.text.CharSequenceUtil; import java.io.Serial; +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.v7.core.text.CharSequenceUtil.isSubEquals; /** * 字符串查找器 @@ -44,6 +48,8 @@ public class StrFinder extends TextFinder { private final CharSequence strToFind; private final boolean caseInsensitive; + private Map forwardOffsetMap; + private Map reverseOffsetMap; /** * 构造 @@ -61,23 +67,54 @@ public class StrFinder extends TextFinder { public int start(int from) { Assert.notNull(this.text, "Text to find must be not null!"); final int subLen = strToFind.length(); + final int textLen = text.length(); - if (from < 0) { - from = 0; - } - int endLimit = getValidEndIndex(); + // 基于Sunday算法实现高效子串查询 if (negative) { - for (int i = from; i > endLimit; i--) { - if (CharSequenceUtil.isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) { + if (this.reverseOffsetMap == null) { + this.reverseOffsetMap = buildReverseOffsetMap(strToFind, caseInsensitive); + } + int maxIndex = textLen - subLen; + if (from > maxIndex) { + from = maxIndex; + } + int i = from; + while (i >= 0) { + if (isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) { return i; } + if (i - 1 < 0) { + break; + } + char preChar = text.charAt(i - 1); + int jump = reverseOffsetMap.getOrDefault( + caseInsensitive ? Character.toLowerCase(preChar) : preChar, + subLen + 1 + ); + i -= jump; } } else { - endLimit = endLimit - subLen + 1; - for (int i = from; i < endLimit; i++) { - if (CharSequenceUtil.isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) { + if (this.forwardOffsetMap == null) { + this.forwardOffsetMap = buildForwardOffsetMap(strToFind, caseInsensitive); + } + if (from < 0) { + from = 0; + } + int endLimit = textLen - subLen; + int i = from; + while (i <= endLimit) { + if (isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) { return i; } + if (i + subLen >= textLen) { + break; + } + char nextChar = text.charAt(i + subLen); + int jump = forwardOffsetMap.getOrDefault( + caseInsensitive ? Character.toLowerCase(nextChar) : nextChar, + subLen + 1 + ); + i += jump; } } @@ -91,4 +128,44 @@ public class StrFinder extends TextFinder { } return start + strToFind.length(); } + + /** + * 构建正向偏移表 + */ + private static Map buildForwardOffsetMap(CharSequence pattern, boolean caseInsensitive) { + int m = pattern.length(); + Map map = new HashMap<>(Math.min(m, 128)); + + for (int i = 0; i < m; i++) { + char c = pattern.charAt(i); + int jump = m - i; + + if (caseInsensitive) { + map.put(Character.toLowerCase(c), jump); + } else { + map.put(c, jump); + } + } + return map; + } + + /** + * 构建反向偏移表 + */ + private static Map buildReverseOffsetMap(CharSequence pattern, boolean caseInsensitive) { + int m = pattern.length(); + Map map = new HashMap<>(Math.min(m, 128)); + + for (int i = m - 1; i >= 0; i--) { + char c = pattern.charAt(i); + int jump = i + 1; + + if (caseInsensitive) { + map.put(Character.toLowerCase(c), jump); + } else { + map.put(c, jump); + } + } + return map; + } }