浏览代码

add FoundWord

Looly 5 年之前
父节点
当前提交
850c766213

+ 2 - 1
CHANGELOG.md

@@ -3,7 +3,7 @@
 
 
 -------------------------------------------------------------------------------------------------------------
 -------------------------------------------------------------------------------------------------------------
 
 
-# 5.5.3 (2020-12-07)
+# 5.5.3 (2020-12-08)
 
 
 ### 新特性
 ### 新特性
 * 【core   】     IdcardUtil增加行政区划83(issue#1277@Github)
 * 【core   】     IdcardUtil增加行政区划83(issue#1277@Github)
@@ -12,6 +12,7 @@
 * 【db     】     Db增加使用sql的page方法(issue#247@Gitee)
 * 【db     】     Db增加使用sql的page方法(issue#247@Gitee)
 * 【cache  】     CacheObj的isExpired()逻辑修改(issue#1295@Github)
 * 【cache  】     CacheObj的isExpired()逻辑修改(issue#1295@Github)
 * 【json   】     JSONStrFormater改为JSONStrFormatter
 * 【json   】     JSONStrFormater改为JSONStrFormatter
+* 【dfa    】     增加FoundWord(pr#1290@Github)
 
 
 ### Bug修复
 ### Bug修复
 * 【cache  】     修复Cache中get重复misCount计数问题(issue#1281@Github)
 * 【cache  】     修复Cache中get重复misCount计数问题(issue#1281@Github)

+ 34 - 0
hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java

@@ -0,0 +1,34 @@
+package cn.hutool.core.lang;
+
+/**
+ * 片段默认实现
+ *
+ * @param <T> 数字类型,用于表示位置index
+ * @author looly
+ * @since 5.5.3
+ */
+public class DefaultSegment<T extends Number> implements Segment<T> {
+
+	protected T startIndex;
+	protected T endIndex;
+
+	/**
+	 * 构造
+	 * @param startIndex 起始位置
+	 * @param endIndex 结束位置
+	 */
+	public DefaultSegment(T startIndex, T endIndex) {
+		this.startIndex = startIndex;
+		this.endIndex = endIndex;
+	}
+
+	@Override
+	public T getStartIndex() {
+		return this.startIndex;
+	}
+
+	@Override
+	public T getEndIndex() {
+		return this.endIndex;
+	}
+}

+ 41 - 0
hutool-core/src/main/java/cn/hutool/core/lang/Segment.java

@@ -0,0 +1,41 @@
+package cn.hutool.core.lang;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.NumberUtil;
+
+import java.lang.reflect.Type;
+
+/**
+ * 片段表示,用于表示文本、集合等数据结构的一个区间。
+ * @param <T> 数字类型,用于表示位置index
+ *
+ * @author looly
+ * @since 5.5.3
+ */
+public interface Segment<T extends Number> {
+
+	/**
+	 * 获取起始位置
+	 *
+	 * @return 起始位置
+	 */
+	T getStartIndex();
+
+	/**
+	 * 获取结束位置
+	 *
+	 * @return 结束位置
+	 */
+	T getEndIndex();
+
+	/**
+	 * 片段长度,默认计算方法为abs({@link #getEndIndex()} - {@link #getEndIndex()})
+	 *
+	 * @return 片段长度
+	 */
+	default T length(){
+		final T start = Assert.notNull(getStartIndex(), "Start index must be not null!");
+		final T end = Assert.notNull(getEndIndex(), "End index must be not null!");
+		return Convert.convert((Type) start.getClass(), NumberUtil.sub(end, start).abs());
+	}
+}

+ 5 - 3
hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java

@@ -523,7 +523,8 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
 	 * @param minimumCapacity 最小容量
 	 * @param minimumCapacity 最小容量
 	 */
 	 */
 	private void ensureCapacity(int minimumCapacity) {
 	private void ensureCapacity(int minimumCapacity) {
-		if (minimumCapacity > value.length) {
+		// overflow-conscious code
+		if (minimumCapacity - value.length < 0) {
 			expandCapacity(minimumCapacity);
 			expandCapacity(minimumCapacity);
 		}
 		}
 	}
 	}
@@ -535,8 +536,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
 	 * @param minimumCapacity 需要扩展的最小容量
 	 * @param minimumCapacity 需要扩展的最小容量
 	 */
 	 */
 	private void expandCapacity(int minimumCapacity) {
 	private void expandCapacity(int minimumCapacity) {
-		int newCapacity = value.length * 2 + 2;
-		if (newCapacity < minimumCapacity) {
+		int newCapacity = (value.length << 1) + 2;
+		// overflow-conscious code
+		if (newCapacity - minimumCapacity < 0) {
 			newCapacity = minimumCapacity;
 			newCapacity = minimumCapacity;
 		}
 		}
 		if (newCapacity < 0) {
 		if (newCapacity < 0) {

+ 14 - 1
hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java

@@ -383,7 +383,7 @@ public class ObjectUtil {
 	 * 克隆对象<br>
 	 * 克隆对象<br>
 	 * 如果对象实现Cloneable接口,调用其clone方法<br>
 	 * 如果对象实现Cloneable接口,调用其clone方法<br>
 	 * 如果实现Serializable接口,执行深度克隆<br>
 	 * 如果实现Serializable接口,执行深度克隆<br>
-	 * 否则返回<code>null</code>
+	 * 否则返回{@code null}
 	 *
 	 *
 	 * @param <T> 对象类型
 	 * @param <T> 对象类型
 	 * @param obj 被克隆对象
 	 * @param obj 被克隆对象
@@ -607,10 +607,23 @@ public class ObjectUtil {
 	}
 	}
 
 
 	/**
 	/**
+	 * 是否存在{@code null}对象,通过{@link ObjectUtil#isNull(Object)} 判断元素
+	 *
+	 * @param objs 被检查对象
+	 * @return 是否存在
+	 * @since 5.5.3
+	 * @see ArrayUtil#hasNull(Object[]) 
+	 */
+	public static boolean hasNull(Object... objs) {
+		return ArrayUtil.hasNull(objs);
+	}
+
+	/**
 	 * 是否存在{@code null}或空对象,通过{@link ObjectUtil#isEmpty(Object)} 判断元素
 	 * 是否存在{@code null}或空对象,通过{@link ObjectUtil#isEmpty(Object)} 判断元素
 	 *
 	 *
 	 * @param objs 被检查对象
 	 * @param objs 被检查对象
 	 * @return 是否存在
 	 * @return 是否存在
+	 * @see ArrayUtil#hasEmpty(Object...)
 	 */
 	 */
 	public static boolean hasEmpty(Object... objs) {
 	public static boolean hasEmpty(Object... objs) {
 		return ArrayUtil.hasEmpty(objs);
 		return ArrayUtil.hasEmpty(objs);

+ 51 - 53
hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java

@@ -105,7 +105,7 @@ public class StrUtil {
 	public static final char C_COLON = CharUtil.COLON;
 	public static final char C_COLON = CharUtil.COLON;
 
 
 	/**
 	/**
-	 * 字符常量:艾特 <code>'@'</code>
+	 * 字符常量:艾特 {@code '@'}
 	 */
 	 */
 	public static final char C_AT = CharUtil.AT;
 	public static final char C_AT = CharUtil.AT;
 
 
@@ -210,7 +210,7 @@ public class StrUtil {
 	public static final String COLON = ":";
 	public static final String COLON = ":";
 
 
 	/**
 	/**
-	 * 字符串常量:艾特 <code>"@"</code>
+	 * 字符串常量:艾特 {@code "@"}
 	 */
 	 */
 	public static final String AT = "@";
 	public static final String AT = "@";
 
 
@@ -246,7 +246,7 @@ public class StrUtil {
 	public static final String HTML_GT = "&gt;";
 	public static final String HTML_GT = "&gt;";
 
 
 	/**
 	/**
-	 * 字符串常量:空 JSON <code>"{}"</code>
+	 * 字符串常量:空 JSON {@code "{}"}
 	 */
 	 */
 	public static final String EMPTY_JSON = "{}";
 	public static final String EMPTY_JSON = "{}";
 
 
@@ -542,7 +542,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 如果字符串是 <code>null</code>,则返回指定默认字符串,否则返回字符串本身。
+	 * 如果字符串是 {@code null},则返回指定默认字符串,否则返回字符串本身。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * nullToDefault(null, &quot;default&quot;)  = &quot;default&quot;
 	 * nullToDefault(null, &quot;default&quot;)  = &quot;default&quot;
@@ -560,7 +560,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 如果字符串是<code>null</code>或者&quot;&quot;,则返回指定默认字符串,否则返回字符串本身。
+	 * 如果字符串是{@code null}或者&quot;&quot;,则返回指定默认字符串,否则返回字符串本身。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * emptyToDefault(null, &quot;default&quot;)  = &quot;default&quot;
 	 * emptyToDefault(null, &quot;default&quot;)  = &quot;default&quot;
@@ -579,7 +579,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 如果字符串是<code>null</code>或者&quot;&quot;或者空白,则返回指定默认字符串,否则返回字符串本身。
+	 * 如果字符串是{@code null}或者&quot;&quot;或者空白,则返回指定默认字符串,否则返回字符串本身。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * emptyToDefault(null, &quot;default&quot;)  = &quot;default&quot;
 	 * emptyToDefault(null, &quot;default&quot;)  = &quot;default&quot;
@@ -598,7 +598,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 当给定字符串为空字符串时,转换为<code>null</code>
+	 * 当给定字符串为空字符串时,转换为{@code null}
 	 *
 	 *
 	 * @param str 被转换的字符串
 	 * @param str 被转换的字符串
 	 * @return 转换后的字符串
 	 * @return 转换后的字符串
@@ -774,10 +774,10 @@ public class StrUtil {
 	// ------------------------------------------------------------------------ Trim
 	// ------------------------------------------------------------------------ Trim
 
 
 	/**
 	/**
-	 * 除去字符串头尾部的空白,如果字符串是<code>null</code>,依然返回<code>null</code>
+	 * 除去字符串头尾部的空白,如果字符串是{@code null},依然返回{@code null}
 	 *
 	 *
 	 * <p>
 	 * <p>
-	 * 注意,和<code>String.trim</code>不同,此方法使用<code>NumberUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+	 * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * trim(null)          = null
 	 * trim(null)          = null
@@ -788,7 +788,7 @@ public class StrUtil {
 	 * </pre>
 	 * </pre>
 	 *
 	 *
 	 * @param str 要处理的字符串
 	 * @param str 要处理的字符串
-	 * @return 除去头尾空白的字符串,如果原字串为<code>null</code>,则返回<code>null</code>
+	 * @return 除去头尾空白的字符串,如果原字串为{@code null},则返回{@code null}
 	 */
 	 */
 	public static String trim(CharSequence str) {
 	public static String trim(CharSequence str) {
 		return (null == str) ? null : trim(str, 0);
 		return (null == str) ? null : trim(str, 0);
@@ -813,7 +813,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 除去字符串头尾部的空白,如果字符串是{@code null},返回<code>""</code>
+	 * 除去字符串头尾部的空白,如果字符串是{@code null},返回{@code ""}
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * StrUtil.trimToEmpty(null)          = ""
 	 * StrUtil.trimToEmpty(null)          = ""
@@ -852,10 +852,10 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 除去字符串头部的空白,如果字符串是<code>null</code>,则返回<code>null</code>
+	 * 除去字符串头部的空白,如果字符串是{@code null},则返回{@code null}
 	 *
 	 *
 	 * <p>
 	 * <p>
-	 * 注意,和<code>String.trim</code>不同,此方法使用<code>CharUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+	 * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * trimStart(null)         = null
 	 * trimStart(null)         = null
@@ -867,17 +867,17 @@ public class StrUtil {
 	 * </pre>
 	 * </pre>
 	 *
 	 *
 	 * @param str 要处理的字符串
 	 * @param str 要处理的字符串
-	 * @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<code>""</code>,则返回 <code>null</code>
+	 * @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
 	 */
 	 */
 	public static String trimStart(CharSequence str) {
 	public static String trimStart(CharSequence str) {
 		return trim(str, -1);
 		return trim(str, -1);
 	}
 	}
 
 
 	/**
 	/**
-	 * 除去字符串尾部的空白,如果字符串是<code>null</code>,则返回<code>null</code>
+	 * 除去字符串尾部的空白,如果字符串是{@code null},则返回{@code null}
 	 *
 	 *
 	 * <p>
 	 * <p>
-	 * 注意,和<code>String.trim</code>不同,此方法使用<code>CharUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+	 * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
 	 *
 	 *
 	 * <pre>
 	 * <pre>
 	 * trimEnd(null)       = null
 	 * trimEnd(null)       = null
@@ -889,47 +889,45 @@ public class StrUtil {
 	 * </pre>
 	 * </pre>
 	 *
 	 *
 	 * @param str 要处理的字符串
 	 * @param str 要处理的字符串
-	 * @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<code>""</code>,则返回 <code>null</code>
+	 * @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
 	 */
 	 */
 	public static String trimEnd(CharSequence str) {
 	public static String trimEnd(CharSequence str) {
 		return trim(str, 1);
 		return trim(str, 1);
 	}
 	}
 
 
 	/**
 	/**
-	 * 除去字符串头尾部的空白符,如果字符串是<code>null</code>,依然返回<code>null</code>
+	 * 除去字符串头尾部的空白符,如果字符串是{@code null},依然返回{@code null}
 	 *
 	 *
 	 * @param str  要处理的字符串
 	 * @param str  要处理的字符串
-	 * @param mode <code>-1</code>表示trimStart,<code>0</code>表示trim全部, <code>1</code>表示trimEnd
-	 * @return 除去指定字符后的的字符串,如果原字串为<code>null</code>,则返回<code>null</code>
+	 * @param mode {@code -1}表示trimStart,{@code 0}表示trim全部, {@code 1}表示trimEnd
+	 * @return 除去指定字符后的的字符串,如果原字串为{@code null},则返回{@code null}
 	 */
 	 */
 	public static String trim(CharSequence str, int mode) {
 	public static String trim(CharSequence str, int mode) {
+		String result;
 		if (str == null) {
 		if (str == null) {
-			return null;
-		}
-
-		int length = str.length();
-		int start = 0;
-		int end = length;
-
-		// 扫描字符串头部
-		if (mode <= 0) {
-			while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
-				start++;
+			result = null;
+		} else {
+			int length = str.length();
+			int start = 0;
+			int end = length;// 扫描字符串头部
+			if (mode <= 0) {
+				while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
+					start++;
+				}
+			}// 扫描字符串尾部
+			if (mode >= 0) {
+				while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
+					end--;
+				}
 			}
 			}
-		}
-
-		// 扫描字符串尾部
-		if (mode >= 0) {
-			while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
-				end--;
+			if ((start > 0) || (end < length)) {
+				result = str.toString().substring(start, end);
+			} else {
+				result = str.toString();
 			}
 			}
 		}
 		}
 
 
-		if ((start > 0) || (end < length)) {
-			return str.toString().substring(start, end);
-		}
-
-		return str.toString();
+		return result;
 	}
 	}
 
 
 	/**
 	/**
@@ -1251,7 +1249,7 @@ public class StrUtil {
 	}
 	}
 
 
 	/**
 	/**
-	 * 是否包含特定字符,忽略大小写,如果给定两个参数都为<code>null</code>,返回true
+	 * 是否包含特定字符,忽略大小写,如果给定两个参数都为{@code null},返回true
 	 *
 	 *
 	 * @param str     被检测字符串
 	 * @param str     被检测字符串
 	 * @param testStr 被测试是否包含的字符串
 	 * @param testStr 被测试是否包含的字符串
@@ -1971,7 +1969,7 @@ public class StrUtil {
 	 * abcdefgh 2 3 =》 c <br>
 	 * abcdefgh 2 3 =》 c <br>
 	 * abcdefgh 2 -3 =》 cde <br>
 	 * abcdefgh 2 -3 =》 cde <br>
 	 *
 	 *
-	 * @param str       String
+	 * @param str              String
 	 * @param fromIndexInclude 开始的index(包括)
 	 * @param fromIndexInclude 开始的index(包括)
 	 * @param toIndexExclude   结束的index(不包括)
 	 * @param toIndexExclude   结束的index(不包括)
 	 * @return 字串
 	 * @return 字串
@@ -2094,7 +2092,7 @@ public class StrUtil {
 	/**
 	/**
 	 * 切割指定位置之前部分的字符串
 	 * 切割指定位置之前部分的字符串
 	 *
 	 *
-	 * @param string  字符串
+	 * @param string         字符串
 	 * @param toIndexExclude 切割到的位置(不包括)
 	 * @param toIndexExclude 切割到的位置(不包括)
 	 * @return 切割后的剩余的前半部分字符串
 	 * @return 切割后的剩余的前半部分字符串
 	 */
 	 */
@@ -2406,12 +2404,12 @@ public class StrUtil {
 
 
 		final List<String> result = new LinkedList<>();
 		final List<String> result = new LinkedList<>();
 		final String[] split = split(str, prefix);
 		final String[] split = split(str, prefix);
-		if(prefix.equals(suffix)){
+		if (prefix.equals(suffix)) {
 			// 前后缀字符相同,单独处理
 			// 前后缀字符相同,单独处理
 			for (int i = 1, length = split.length - 1; i < length; i += 2) {
 			for (int i = 1, length = split.length - 1; i < length; i += 2) {
 				result.add(split[i]);
 				result.add(split[i]);
 			}
 			}
-		} else{
+		} else {
 			int suffixIndex;
 			int suffixIndex;
 			for (String fragment : split) {
 			for (String fragment : split) {
 				suffixIndex = fragment.indexOf(suffix.toString());
 				suffixIndex = fragment.indexOf(suffix.toString());
@@ -2441,7 +2439,7 @@ public class StrUtil {
 	 * StrUtil.subBetweenAll("#hello# world#!", "#");   = ["hello"]
 	 * StrUtil.subBetweenAll("#hello# world#!", "#");   = ["hello"]
 	 * </pre>
 	 * </pre>
 	 *
 	 *
-	 * @param str    被切割的字符串
+	 * @param str             被切割的字符串
 	 * @param prefixAndSuffix 截取开始和结束的字符串标识
 	 * @param prefixAndSuffix 截取开始和结束的字符串标识
 	 * @return 截取后的字符串
 	 * @return 截取后的字符串
 	 * @author gotanks
 	 * @author gotanks
@@ -2620,7 +2618,7 @@ public class StrUtil {
 	 *
 	 *
 	 * @param str1 要比较的字符串1
 	 * @param str1 要比较的字符串1
 	 * @param str2 要比较的字符串2
 	 * @param str2 要比较的字符串2
-	 * @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
+	 * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
 	 */
 	 */
 	public static boolean equals(CharSequence str1, CharSequence str2) {
 	public static boolean equals(CharSequence str1, CharSequence str2) {
 		return equals(str1, str2, false);
 		return equals(str1, str2, false);
@@ -2639,7 +2637,7 @@ public class StrUtil {
 	 *
 	 *
 	 * @param str1 要比较的字符串1
 	 * @param str1 要比较的字符串1
 	 * @param str2 要比较的字符串2
 	 * @param str2 要比较的字符串2
-	 * @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
+	 * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
 	 */
 	 */
 	public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
 	public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
 		return equals(str1, str2, true);
 		return equals(str1, str2, true);
@@ -2651,7 +2649,7 @@ public class StrUtil {
 	 * @param str1       要比较的字符串1
 	 * @param str1       要比较的字符串1
 	 * @param str2       要比较的字符串2
 	 * @param str2       要比较的字符串2
 	 * @param ignoreCase 是否忽略大小写
 	 * @param ignoreCase 是否忽略大小写
-	 * @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
+	 * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
 	 * @since 3.2.0
 	 * @since 3.2.0
 	 */
 	 */
 	public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
 	public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
@@ -3431,7 +3429,7 @@ public class StrUtil {
 	 * StrUtil.padAfter("123", 2, '0');//"23"
 	 * StrUtil.padAfter("123", 2, '0');//"23"
 	 * </pre>
 	 * </pre>
 	 *
 	 *
-	 * @param str       字符串,如果为<code>null</code>,直接返回null
+	 * @param str       字符串,如果为{@code null},直接返回null
 	 * @param minLength 最小长度
 	 * @param minLength 最小长度
 	 * @param padChar   补充的字符
 	 * @param padChar   补充的字符
 	 * @return 补充后的字符串
 	 * @return 补充后的字符串
@@ -3459,7 +3457,7 @@ public class StrUtil {
 	 * StrUtil.padAfter("123", 2, "ABC");//"23"
 	 * StrUtil.padAfter("123", 2, "ABC");//"23"
 	 * </pre>
 	 * </pre>
 	 *
 	 *
-	 * @param str       字符串,如果为<code>null</code>,直接返回null
+	 * @param str       字符串,如果为{@code null},直接返回null
 	 * @param minLength 最小长度
 	 * @param minLength 最小长度
 	 * @param padStr    补充的字符
 	 * @param padStr    补充的字符
 	 * @return 补充后的字符串
 	 * @return 补充后的字符串

+ 36 - 24
hutool-dfa/src/main/java/cn/hutool/dfa/FoundWord.java

@@ -1,49 +1,61 @@
 package cn.hutool.dfa;
 package cn.hutool.dfa;
 
 
+import cn.hutool.core.lang.DefaultSegment;
+
 /**
 /**
- * @author 肖海斌
  * <p>
  * <p>
- * 匹配到的敏感词,包含敏感词,text中匹配敏感词的内容,以及匹配内容在text中的下标,
- * 下标可以用来做敏感词的进一步处理,如果替换成**
+ * 匹配到的单词,包含单词,text中匹配单词的内容,以及匹配内容在text中的下标,
+ * 下标可以用来做单词的进一步处理,如果替换成**
+ *
+ * @author 肖海斌
  */
  */
-public class FoundWord {
-	/**
-	 * 生效的敏感词
-	 */
-	private String word;
+public class FoundWord extends DefaultSegment<Integer> {
 	/**
 	/**
-	 * 敏感词匹配到的内容
+	 * 生效的单词,即单词树中的词
 	 */
 	 */
-	private String foundWord;
+	private final String word;
 	/**
 	/**
-	 * 匹配内容在待分析字符串中的开始位置
+	 * 单词匹配到的内容,即文中的单词
 	 */
 	 */
-	private int startIndex;
+	private final String foundWord;
+
 	/**
 	/**
-	 * 匹配内容在待分析字符串中的结束位置
+	 * 构造
+	 *
+	 * @param word 生效的单词,即单词树中的词
+	 * @param foundWord 单词匹配到的内容,即文中的单词
+	 * @param startIndex 起始位置(包含)
+	 * @param endIndex 结束位置(包含)
 	 */
 	 */
-	private int endIndex;
-
-	public FoundWord(String word, String foundWord, int start, int end) {
+	public FoundWord(String word, String foundWord, int startIndex, int endIndex) {
+		super(startIndex, endIndex);
 		this.word = word;
 		this.word = word;
 		this.foundWord = foundWord;
 		this.foundWord = foundWord;
-		this.startIndex = start;
-		this.endIndex = end;
 	}
 	}
 
 
+	/**
+	 * 获取生效的单词,即单词树中的词
+	 *
+	 * @return 生效的单词
+	 */
 	public String getWord() {
 	public String getWord() {
 		return word;
 		return word;
 	}
 	}
 
 
+	/**
+	 * 获取单词匹配到的内容,即文中的单词
+	 * @return 单词匹配到的内容
+	 */
 	public String getFoundWord() {
 	public String getFoundWord() {
 		return foundWord;
 		return foundWord;
 	}
 	}
 
 
-	public int getStartIndex() {
-		return startIndex;
-	}
-
-	public int getEndIndex() {
-		return endIndex;
+	/**
+	 * 默认的,只输出匹配到的关键字
+	 * @return 匹配到的关键字
+	 */
+	@Override
+	public String toString() {
+		return this.foundWord;
 	}
 	}
 }
 }

+ 101 - 13
hutool-dfa/src/main/java/cn/hutool/dfa/SensitiveUtil.java

@@ -1,6 +1,6 @@
 package cn.hutool.dfa;
 package cn.hutool.dfa;
 
 
-import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Filter;
 import cn.hutool.core.lang.Filter;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
@@ -25,7 +25,7 @@ public final class SensitiveUtil {
 	 * @return 是否已经被初始化
 	 * @return 是否已经被初始化
 	 */
 	 */
 	public static boolean isInited() {
 	public static boolean isInited() {
-		return !sensitiveTree.isEmpty();
+		return false == sensitiveTree.isEmpty();
 	}
 	}
 
 
 	/**
 	/**
@@ -117,32 +117,70 @@ public final class SensitiveUtil {
 	 *
 	 *
 	 * @param text 文本
 	 * @param text 文本
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundFirstSensitive(String)}
 	 */
 	 */
-	public static FoundWord getFindedFirstSensitive(String text) {
+	@Deprecated
+	public static String getFindedFirstSensitive(String text) {
 		return sensitiveTree.match(text);
 		return sensitiveTree.match(text);
 	}
 	}
 
 
 	/**
 	/**
 	 * 查找敏感词,返回找到的第一个敏感词
 	 * 查找敏感词,返回找到的第一个敏感词
 	 *
 	 *
+	 * @param text 文本
+	 * @return 敏感词
+	 * @since 5.5.3
+	 */
+	public static FoundWord getFoundFirstSensitive(String text) {
+		return sensitiveTree.matchWord(text);
+	}
+
+	/**
+	 * 查找敏感词,返回找到的第一个敏感词
+	 *
 	 * @param obj bean,会被转为JSON字符串
 	 * @param obj bean,会被转为JSON字符串
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundFirstSensitive(Object)}
 	 */
 	 */
-	public static FoundWord getFindedFirstSensitive(Object obj) {
+	@Deprecated
+	public static String getFindedFirstSensitive(Object obj) {
 		return sensitiveTree.match(JSONUtil.toJsonStr(obj));
 		return sensitiveTree.match(JSONUtil.toJsonStr(obj));
 	}
 	}
 
 
 	/**
 	/**
+	 * 查找敏感词,返回找到的第一个敏感词
+	 *
+	 * @param obj bean,会被转为JSON字符串
+	 * @return 敏感词
+	 */
+	public static FoundWord getFoundFirstSensitive(Object obj) {
+		return sensitiveTree.matchWord(JSONUtil.toJsonStr(obj));
+	}
+
+	/**
 	 * 查找敏感词,返回找到的所有敏感词
 	 * 查找敏感词,返回找到的所有敏感词
 	 *
 	 *
 	 * @param text 文本
 	 * @param text 文本
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundAllSensitive(String)}
 	 */
 	 */
-	public static List<FoundWord> getFindedAllSensitive(String text) {
+	@Deprecated
+	public static List<String> getFindedAllSensitive(String text) {
 		return sensitiveTree.matchAll(text);
 		return sensitiveTree.matchAll(text);
 	}
 	}
 
 
 	/**
 	/**
+	 * 查找敏感词,返回找到的所有敏感词
+	 *
+	 * @param text 文本
+	 * @return 敏感词
+	 * @since 5.5.3
+	 */
+	public static List<FoundWord> getFoundAllSensitive(String text) {
+		return sensitiveTree.matchAllWords(text);
+	}
+
+	/**
 	 * 查找敏感词,返回找到的所有敏感词<br>
 	 * 查找敏感词,返回找到的所有敏感词<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
@@ -151,22 +189,51 @@ public final class SensitiveUtil {
 	 * @param isDensityMatch 是否使用密集匹配原则
 	 * @param isDensityMatch 是否使用密集匹配原则
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundAllSensitive(String, boolean, boolean)}
 	 */
 	 */
-	public static List<FoundWord> getFindedAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
+	@Deprecated
+	public static List<String> getFindedAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
 		return sensitiveTree.matchAll(text, -1, isDensityMatch, isGreedMatch);
 		return sensitiveTree.matchAll(text, -1, isDensityMatch, isGreedMatch);
 	}
 	}
 
 
 	/**
 	/**
+	 * 查找敏感词,返回找到的所有敏感词<br>
+	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
+	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
+	 *
+	 * @param text           文本
+	 * @param isDensityMatch 是否使用密集匹配原则
+	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
+	 * @return 敏感词
+	 */
+	public static List<FoundWord> getFoundAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
+		return sensitiveTree.matchAllWords(text, -1, isDensityMatch, isGreedMatch);
+	}
+
+	/**
 	 * 查找敏感词,返回找到的所有敏感词
 	 * 查找敏感词,返回找到的所有敏感词
 	 *
 	 *
 	 * @param bean 对象,会被转为JSON
 	 * @param bean 对象,会被转为JSON
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundAllSensitive(Object)}
 	 */
 	 */
-	public static List<FoundWord> getFindedAllSensitive(Object bean) {
+	@Deprecated
+	public static List<String> getFindedAllSensitive(Object bean) {
 		return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean));
 		return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean));
 	}
 	}
 
 
 	/**
 	/**
+	 * 查找敏感词,返回找到的所有敏感词
+	 *
+	 * @param bean 对象,会被转为JSON
+	 * @return 敏感词
+	 * @since 5.5.3
+	 */
+	public static List<FoundWord> getFoundAllSensitive(Object bean) {
+		return sensitiveTree.matchAllWords(JSONUtil.toJsonStr(bean));
+	}
+
+	/**
 	 * 查找敏感词,返回找到的所有敏感词<br>
 	 * 查找敏感词,返回找到的所有敏感词<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
@@ -175,9 +242,26 @@ public final class SensitiveUtil {
 	 * @param isDensityMatch 是否使用密集匹配原则
 	 * @param isDensityMatch 是否使用密集匹配原则
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @return 敏感词
 	 * @return 敏感词
+	 * @deprecated 请使用 {@link #getFoundAllSensitive(Object, boolean, boolean)}
 	 */
 	 */
-	public static List<FoundWord> getFindedAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
-		return getFindedAllSensitive(JSONUtil.toJsonStr(bean), isDensityMatch, isGreedMatch);
+	@Deprecated
+	public static List<String> getFindedAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
+		return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean), -1, isDensityMatch, isGreedMatch);
+	}
+
+	/**
+	 * 查找敏感词,返回找到的所有敏感词<br>
+	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
+	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
+	 *
+	 * @param bean           对象,会被转为JSON
+	 * @param isDensityMatch 是否使用密集匹配原则
+	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
+	 * @return 敏感词
+	 * @since 5.5.3
+	 */
+	public static List<FoundWord> getFoundAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
+		return getFoundAllSensitive(JSONUtil.toJsonStr(bean), isDensityMatch, isGreedMatch);
 	}
 	}
 
 
 	/**
 	/**
@@ -191,23 +275,27 @@ public final class SensitiveUtil {
 	 */
 	 */
 	public static <T> T sensitiveFilter(T bean, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
 	public static <T> T sensitiveFilter(T bean, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
 		String jsonText = JSONUtil.toJsonStr(bean);
 		String jsonText = JSONUtil.toJsonStr(bean);
-		Class<T> c = (Class) bean.getClass();
+		@SuppressWarnings("unchecked")
+		final Class<T> c = (Class<T>) bean.getClass();
 		return JSONUtil.toBean(sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor), c);
 		return JSONUtil.toBean(sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor), c);
 	}
 	}
 
 
 	/**
 	/**
+	 * 处理过滤文本中的敏感词,默认替换成*
+	 *
 	 * @param text               文本
 	 * @param text               文本
 	 * @param isGreedMatch       贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
 	 * @param isGreedMatch       贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
 	 * @param sensitiveProcessor 敏感词处理器,默认按匹配内容的字符数替换成*
 	 * @param sensitiveProcessor 敏感词处理器,默认按匹配内容的字符数替换成*
 	 * @return 敏感词过滤处理后的文本
 	 * @return 敏感词过滤处理后的文本
 	 */
 	 */
 	public static String sensitiveFilter(String text, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
 	public static String sensitiveFilter(String text, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
-		if (null == text || text.trim().equals("")) {
+		if (StrUtil.isEmpty(text)) {
 			return text;
 			return text;
 		}
 		}
+
 		//敏感词过滤场景下,不需要密集匹配
 		//敏感词过滤场景下,不需要密集匹配
-		List<FoundWord> foundWordList = getFindedAllSensitive(text, false, isGreedMatch);
-		if (CollectionUtil.isEmpty(foundWordList)) {
+		List<FoundWord> foundWordList = getFoundAllSensitive(text, false, isGreedMatch);
+		if (CollUtil.isEmpty(foundWordList)) {
 			return text;
 			return text;
 		}
 		}
 		sensitiveProcessor = sensitiveProcessor == null ? new SensitiveProcessor() {
 		sensitiveProcessor = sensitiveProcessor == null ? new SensitiveProcessor() {

+ 75 - 18
hutool-dfa/src/main/java/cn/hutool/dfa/WordTree.java

@@ -1,11 +1,17 @@
 package cn.hutool.dfa;
 package cn.hutool.dfa;
 
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.lang.Filter;
 import cn.hutool.core.lang.Filter;
 import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 
 /**
 /**
  * DFA(Deterministic Finite Automaton 确定有穷自动机)
  * DFA(Deterministic Finite Automaton 确定有穷自动机)
@@ -26,7 +32,7 @@ public class WordTree extends HashMap<Character, WordTree> {
 	private static final long serialVersionUID = -4646423269465809276L;
 	private static final long serialVersionUID = -4646423269465809276L;
 
 
 	/**
 	/**
-	 * 敏感词字符末尾标识,用于标识单词末尾字符
+	 * 词字符末尾标识,用于标识单词末尾字符
 	 */
 	 */
 	private final Set<Character> endCharacterSet = new HashSet<>();
 	private final Set<Character> endCharacterSet = new HashSet<>();
 	/**
 	/**
@@ -62,26 +68,30 @@ public class WordTree extends HashMap<Character, WordTree> {
 	 * 增加一组单词
 	 * 增加一组单词
 	 *
 	 *
 	 * @param words 单词集合
 	 * @param words 单词集合
+	 * @return this
 	 */
 	 */
-	public void addWords(Collection<String> words) {
+	public WordTree addWords(Collection<String> words) {
 		if (false == (words instanceof Set)) {
 		if (false == (words instanceof Set)) {
 			words = new HashSet<>(words);
 			words = new HashSet<>(words);
 		}
 		}
 		for (String word : words) {
 		for (String word : words) {
 			addWord(word);
 			addWord(word);
 		}
 		}
+		return this;
 	}
 	}
 
 
 	/**
 	/**
 	 * 增加一组单词
 	 * 增加一组单词
 	 *
 	 *
 	 * @param words 单词数组
 	 * @param words 单词数组
+	 *              @return this
 	 */
 	 */
-	public void addWords(String... words) {
+	public WordTree addWords(String... words) {
 		HashSet<String> wordsSet = CollectionUtil.newHashSet(words);
 		HashSet<String> wordsSet = CollectionUtil.newHashSet(words);
 		for (String word : wordsSet) {
 		for (String word : wordsSet) {
 			addWord(word);
 			addWord(word);
 		}
 		}
+		return this;
 	}
 	}
 
 
 	/**
 	/**
@@ -89,7 +99,7 @@ public class WordTree extends HashMap<Character, WordTree> {
 	 *
 	 *
 	 * @param word 单词
 	 * @param word 单词
 	 */
 	 */
-	public void addWord(String word) {
+	public WordTree addWord(String word) {
 		final Filter<Character> charFilter = this.charFilter;
 		final Filter<Character> charFilter = this.charFilter;
 		WordTree parent = null;
 		WordTree parent = null;
 		WordTree current = this;
 		WordTree current = this;
@@ -112,8 +122,8 @@ public class WordTree extends HashMap<Character, WordTree> {
 		if (null != parent) {
 		if (null != parent) {
 			parent.setEnd(currentChar);
 			parent.setEnd(currentChar);
 		}
 		}
+		return this;
 	}
 	}
-
 	//------------------------------------------------------------------------------- match
 	//------------------------------------------------------------------------------- match
 
 
 	/**
 	/**
@@ -126,7 +136,7 @@ public class WordTree extends HashMap<Character, WordTree> {
 		if (null == text) {
 		if (null == text) {
 			return false;
 			return false;
 		}
 		}
-		return null != match(text);
+		return null != matchWord(text);
 	}
 	}
 
 
 	/**
 	/**
@@ -135,15 +145,24 @@ public class WordTree extends HashMap<Character, WordTree> {
 	 * @param text 被检查的文本
 	 * @param text 被检查的文本
 	 * @return 匹配到的关键字
 	 * @return 匹配到的关键字
 	 */
 	 */
-	public FoundWord match(String text) {
+	public String match(String text) {
+		final FoundWord foundWord = matchWord(text);
+		return null != foundWord ? foundWord.toString() : null;
+	}
+
+	/**
+	 * 获得第一个匹配的关键字
+	 *
+	 * @param text 被检查的文本
+	 * @return 匹配到的关键字
+	 * @since 5.5.3
+	 */
+	public FoundWord matchWord(String text) {
 		if (null == text) {
 		if (null == text) {
 			return null;
 			return null;
 		}
 		}
-		List<FoundWord> matchAll = matchAll(text, 1);
-		if (CollectionUtil.isNotEmpty(matchAll)) {
-			return matchAll.get(0);
-		}
-		return null;
+		final List<FoundWord> matchAll = matchAllWords(text, 1);
+		return CollUtil.get(matchAll, 0);
 	}
 	}
 
 
 	//------------------------------------------------------------------------------- match all
 	//------------------------------------------------------------------------------- match all
@@ -154,22 +173,45 @@ public class WordTree extends HashMap<Character, WordTree> {
 	 * @param text 被检查的文本
 	 * @param text 被检查的文本
 	 * @return 匹配的词列表
 	 * @return 匹配的词列表
 	 */
 	 */
-	public List<FoundWord> matchAll(String text) {
+	public List<String> matchAll(String text) {
 		return matchAll(text, -1);
 		return matchAll(text, -1);
 	}
 	}
 
 
 	/**
 	/**
 	 * 找出所有匹配的关键字
 	 * 找出所有匹配的关键字
 	 *
 	 *
+	 * @param text 被检查的文本
+	 * @return 匹配的词列表
+	 * @since 5.5.3
+	 */
+	public List<FoundWord> matchAllWords(String text) {
+		return matchAllWords(text, -1);
+	}
+
+	/**
+	 * 找出所有匹配的关键字
+	 *
 	 * @param text  被检查的文本
 	 * @param text  被检查的文本
 	 * @param limit 限制匹配个数
 	 * @param limit 限制匹配个数
 	 * @return 匹配的词列表
 	 * @return 匹配的词列表
 	 */
 	 */
-	public List<FoundWord> matchAll(String text, int limit) {
+	public List<String> matchAll(String text, int limit) {
 		return matchAll(text, limit, false, false);
 		return matchAll(text, limit, false, false);
 	}
 	}
 
 
 	/**
 	/**
+	 * 找出所有匹配的关键字
+	 *
+	 * @param text  被检查的文本
+	 * @param limit 限制匹配个数
+	 * @return 匹配的词列表
+	 * @since 5.5.3
+	 */
+	public List<FoundWord> matchAllWords(String text, int limit) {
+		return matchAllWords(text, limit, false, false);
+	}
+
+	/**
 	 * 找出所有匹配的关键字<br>
 	 * 找出所有匹配的关键字<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
 	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
@@ -180,7 +222,24 @@ public class WordTree extends HashMap<Character, WordTree> {
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
 	 * @return 匹配的词列表
 	 * @return 匹配的词列表
 	 */
 	 */
-	public List<FoundWord> matchAll(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
+	public List<String> matchAll(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
+		final List<FoundWord> matchAllWords = matchAllWords(text, limit, isDensityMatch, isGreedMatch);
+		return CollUtil.map(matchAllWords, FoundWord::toString, true);
+	}
+
+	/**
+	 * 找出所有匹配的关键字<br>
+	 * 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
+	 * 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
+	 *
+	 * @param text           被检查的文本
+	 * @param limit          限制匹配个数
+	 * @param isDensityMatch 是否使用密集匹配原则
+	 * @param isGreedMatch   是否使用贪婪匹配(最长匹配)原则
+	 * @return 匹配的词列表
+	 * @since 5.5.3
+	 */
+	public List<FoundWord> matchAllWords(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
 		if (null == text) {
 		if (null == text) {
 			return null;
 			return null;
 		}
 		}
@@ -239,8 +298,6 @@ public class WordTree extends HashMap<Character, WordTree> {
 		}
 		}
 		return foundWords;
 		return foundWords;
 	}
 	}
-
-
 	//--------------------------------------------------------------------------------------- Private method start
 	//--------------------------------------------------------------------------------------- Private method start
 
 
 	/**
 	/**

+ 16 - 20
hutool-dfa/src/test/java/cn/hutool/dfa/test/DfaTest.java

@@ -1,13 +1,10 @@
-package cn.hutool.dfa.test;
+package cn.hutool.dfa;
 
 
-import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.dfa.FoundWord;
-import cn.hutool.dfa.WordTree;
+import cn.hutool.core.collection.CollUtil;
 import org.junit.Assert;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import java.util.List;
 import java.util.List;
-import java.util.stream.Collectors;
 
 
 /**
 /**
  * DFA单元测试
  * DFA单元测试
@@ -29,8 +26,8 @@ public class DfaTest {
 		// 情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词
 		// 情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词
 		// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配
 		// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配
 		// 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短)
 		// 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短)
-		List<FoundWord> matchAll = tree.matchAll(text, -1, false, false);
-		Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "土^豆", "刚出锅"));
+		List<String> matchAll = tree.matchAll(text, -1, false, false);
+		Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "土^豆", "刚出锅"));
 	}
 	}
 
 
 	/**
 	/**
@@ -45,8 +42,8 @@ public class DfaTest {
 		// 情况二:匹配到最短关键词,不跳过已经匹配的关键词
 		// 情况二:匹配到最短关键词,不跳过已经匹配的关键词
 		// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】
 		// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】
 		// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
 		// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
-		List<FoundWord> matchAll = tree.matchAll(text, -1, true, false);
-		Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "土^豆", "刚出锅", "出锅"));
+		List<String> matchAll = tree.matchAll(text, -1, true, false);
+		Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "土^豆", "刚出锅", "出锅"));
 	}
 	}
 
 
 	/**
 	/**
@@ -61,8 +58,8 @@ public class DfaTest {
 		// 情况三:匹配到最长关键词,跳过已经匹配的关键词
 		// 情况三:匹配到最长关键词,跳过已经匹配的关键词
 		// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
 		// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
 		// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
 		// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
-		List<FoundWord> matchAll = tree.matchAll(text, -1, false, true);
-		Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "大土^豆", "刚出锅"));
+		List<String> matchAll = tree.matchAll(text, -1, false, true);
+		Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "大土^豆", "刚出锅"));
 
 
 	}
 	}
 
 
@@ -78,8 +75,8 @@ public class DfaTest {
 		// 情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)
 		// 情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)
 		// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配
 		// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配
 		// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
 		// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
-		List<FoundWord> matchAll = tree.matchAll(text, -1, true, true);
-		Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "大土^豆", "土^豆", "刚出锅", "出锅"));
+		List<String> matchAll = tree.matchAll(text, -1, true, true);
+		Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "大土^豆", "土^豆", "刚出锅", "出锅"));
 
 
 	}
 	}
 
 
@@ -91,21 +88,20 @@ public class DfaTest {
 		WordTree tree = new WordTree();
 		WordTree tree = new WordTree();
 		tree.addWord("tio");
 		tree.addWord("tio");
 
 
-		List<FoundWord> all = tree.matchAll("AAAAAAAt-ioBBBBBBB");
-		Assert.assertEquals(all.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("t-io"));
+		List<String> all = tree.matchAll("AAAAAAAt-ioBBBBBBB");
+		Assert.assertEquals(all, CollUtil.newArrayList("t-io"));
 	}
 	}
 
 
 	@Test
 	@Test
-	public void aTest() {
+	public void aTest(){
 		WordTree tree = new WordTree();
 		WordTree tree = new WordTree();
 		tree.addWord("women");
 		tree.addWord("women");
 		String text = "a WOMEN todo.".toLowerCase();
 		String text = "a WOMEN todo.".toLowerCase();
-		List<FoundWord> matchAll = tree.matchAll(text, -1, false, false);
-		Assert.assertEquals("[women]", matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()).toString());
+		List<String> matchAll = tree.matchAll(text, -1, false, false);
+		Assert.assertEquals("[women]", matchAll.toString());
 	}
 	}
 
 
 	// ----------------------------------------------------------------------------------------------------------
 	// ----------------------------------------------------------------------------------------------------------
-
 	/**
 	/**
 	 * 构建查找树
 	 * 构建查找树
 	 *
 	 *
@@ -121,4 +117,4 @@ public class DfaTest {
 		tree.addWord("出锅");
 		tree.addWord("出锅");
 		return tree;
 		return tree;
 	}
 	}
-}
+}

+ 1 - 2
hutool-dfa/src/test/java/cn/hutool/dfa/test/SensitiveUtilTest.java

@@ -1,6 +1,5 @@
-package cn.hutool.dfa.test;
+package cn.hutool.dfa;
 
 
-import cn.hutool.dfa.SensitiveUtil;
 import org.junit.Assert;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Test;
 
 

+ 2 - 2
hutool-json/src/main/java/cn/hutool/json/JSONUtil.java

@@ -694,12 +694,12 @@ public final class JSONUtil {
 	 * 在需要的时候包装对象<br>
 	 * 在需要的时候包装对象<br>
 	 * 包装包括:
 	 * 包装包括:
 	 * <ul>
 	 * <ul>
-	 * <li><code>null</code> =》 <code>JSONNull.NULL</code></li>
+	 * <li>{@code null} =》 {@code JSONNull.NULL}</li>
 	 * <li>array or collection =》 JSONArray</li>
 	 * <li>array or collection =》 JSONArray</li>
 	 * <li>map =》 JSONObject</li>
 	 * <li>map =》 JSONObject</li>
 	 * <li>standard property (Double, String, et al) =》 原对象</li>
 	 * <li>standard property (Double, String, et al) =》 原对象</li>
 	 * <li>来自于java包 =》 字符串</li>
 	 * <li>来自于java包 =》 字符串</li>
-	 * <li>其它 =》 尝试包装为JSONObject,否则返回<code>null</code></li>
+	 * <li>其它 =》 尝试包装为JSONObject,否则返回{@code null}</li>
 	 * </ul>
 	 * </ul>
 	 *
 	 *
 	 * @param object     被包装的对象
 	 * @param object     被包装的对象