fix:避免调用方 显示调用API 触发查找树 优化;并通过内置锁,避免因并行树优化 可能造成的不可预知结果 和 无效重复的 树优化操作

This commit is contained in:
renyp
2023-03-17 09:55:04 +08:00
27 changed files with 643 additions and 371 deletions

View File

@@ -8,7 +8,7 @@ import cn.hutool.core.collection.partition.RandomAccessPartition;
import cn.hutool.core.comparator.PinyinComparator;
import cn.hutool.core.comparator.PropertyComparator;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.math.PageInfo;
import cn.hutool.core.lang.page.PageInfo;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;

View File

@@ -35,7 +35,7 @@ public class StringConverter extends AbstractConverter {
* @param stringFunction 序列化函数
* @return this
*/
public StringConverter putStringer(Class<?> clazz, Function<Object, String> stringFunction){
public StringConverter putStringer(final Class<?> clazz, final Function<Object, String> stringFunction){
if(null == stringer){
stringer = new HashMap<>();
}

View File

@@ -44,7 +44,7 @@ public class SerializeUtil {
return null;
}
final FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
IoUtil.writeObjects(byteOut, false, (Serializable) obj);
IoUtil.writeObjects(byteOut, false, obj);
return byteOut.toByteArray();
}

View File

@@ -7,9 +7,12 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.LookupFactory;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.reflect.ReflectUtil;
import java.io.Serializable;
import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -31,7 +34,7 @@ public class LambdaFactory {
throw new IllegalAccessException();
}
private static final Map<MutableEntry<Class<?>, Method>, Object> CACHE = new WeakConcurrentMap<>();
private static final Map<MutableEntry<Class<?>, Executable>, Object> CACHE = new WeakConcurrentMap<>();
/**
* 构建Lambda
@@ -61,30 +64,37 @@ public class LambdaFactory {
* 构建Lambda
*
* @param functionInterfaceType 接受Lambda的函数式接口类型
* @param method 方法对象
* @param executable 方法对象,支持构造器
* @param <F> Function类型
* @return 接受Lambda的函数式接口对象
* @param <F> Function类型
*/
@SuppressWarnings("unchecked")
public static <F> F build(final Class<F> functionInterfaceType, final Method method) {
public static <F> F build(final Class<F> functionInterfaceType, final Executable executable) {
Assert.notNull(functionInterfaceType);
Assert.notNull(method);
final MutableEntry<Class<?>, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method);
Assert.notNull(executable);
final MutableEntry<Class<?>, Executable> cacheKey = new MutableEntry<>(functionInterfaceType, executable);
return (F) CACHE.computeIfAbsent(cacheKey, key -> {
final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
.filter(m -> Modifier.isAbstract(m.getModifiers()))
.collect(Collectors.toList());
Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口");
if (!method.isAccessible()) {
method.setAccessible(true);
}
ReflectUtil.setAccessible(executable);
final Method invokeMethod = abstractMethods.get(0);
final MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass());
final MethodHandles.Lookup caller = LookupFactory.lookup(executable.getDeclaringClass());
final String invokeName = invokeMethod.getName();
final MethodType invokedType = methodType(functionInterfaceType);
final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes());
final MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get();
final MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
final MethodHandle implMethod;
final MethodType instantiatedMethodType;
if (executable instanceof Method) {
final Method method = (Method) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflect(method)).get();
instantiatedMethodType = MethodType.methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
} else {
final Constructor<?> constructor = (Constructor<?>) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflectConstructor(constructor)).get();
instantiatedMethodType = MethodType.methodType(constructor.getDeclaringClass(), constructor.getParameterTypes());
}
final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType);
try {
final CallSite callSite = isSerializable ?
@@ -94,7 +104,7 @@ public class LambdaFactory {
invokedType,
samMethodType,
implMethod,
insMethodType,
instantiatedMethodType,
FLAG_SERIALIZABLE
) :
LambdaMetafactory.metafactory(
@@ -103,7 +113,7 @@ public class LambdaFactory {
invokedType,
samMethodType,
implMethod,
insMethodType
instantiatedMethodType
);
//noinspection unchecked
return (F) callSite.getTarget().invoke();

View File

@@ -0,0 +1,128 @@
package cn.hutool.core.lang.page;
/**
* 导航分页信息类<br>
* 根据提供的总页数、每页记录数、导航页码数等信息,生成导航数组。
* <pre>
* [1] 2 3 4 5 >>
* << 1 [2] 3 4 5 >>
* << 1 2 3 4 [5]
* </pre>
*
* @author 莫取网名
*/
public class NavigatePageInfo extends PageInfo {
private final int navigatePageCount; //导航页码数
private int[] navigatePageNumbers; //所有导航页号
/**
* 构造
*
* @param total 总记录数
* @param pageSize 每页显示记录数
* @param navigatePageCount 导航页码数
*/
public NavigatePageInfo(final int total, final int pageSize, final int navigatePageCount) {
super(total, pageSize);
this.navigatePageCount = navigatePageCount;
//基本参数设定之后进行导航页面的计算
calcNavigatePageNumbers();
}
/**
* 得到所有导航页号
*
* @return {int[]}
*/
public int[] getNavigatePageNumbers() {
return navigatePageNumbers;
}
@Override
public NavigatePageInfo setFirstPageNo(final int firstPageNo) {
super.setFirstPageNo(firstPageNo);
return this;
}
@Override
public NavigatePageInfo setPageNo(final int pageNo) {
super.setPageNo(pageNo);
// 重新计算导航
calcNavigatePageNumbers();
return this;
}
@Override
public String toString() {
final StringBuilder str = new StringBuilder();
if (false == isFirstPage()) {
str.append("<< ");
}
if (navigatePageNumbers.length > 0) {
str.append(wrapForDisplay(navigatePageNumbers[0]));
}
for (int i = 1; i < navigatePageNumbers.length; i++) {
str.append(" ").append(wrapForDisplay(navigatePageNumbers[i]));
}
if (false == isLastPage()) {
str.append(" >>");
}
return str.toString();
}
// region ----- private methods
/**
* 用于显示的包装<br>
* 当前页显示'[pageNumber]',否则直接显示
*
* @param pageNumber 页码
* @return 包装的页码
*/
private String wrapForDisplay(final int pageNumber) {
if (this.pageNo == pageNumber) {
return "[" + pageNumber + "]";
}
return String.valueOf(pageNumber);
}
/**
* 计算导航页
*/
private void calcNavigatePageNumbers() {
//当总页数小于或等于导航页码数时,全部显示
if (pageCount <= navigatePageCount) {
navigatePageNumbers = new int[pageCount];
for (int i = 0; i < pageCount; i++) {
navigatePageNumbers[i] = i + 1;
}
} else { //当总页数大于导航页码数时,部分显示
navigatePageNumbers = new int[navigatePageCount];
int startNum = pageNo - navigatePageCount / 2;
int endNum = pageNo + navigatePageCount / 2;
if (startNum < 1) {
startNum = 1;
//(最前navPageCount页
for (int i = 0; i < navigatePageCount; i++) {
navigatePageNumbers[i] = startNum++;
}
} else if (endNum > pageCount) {
endNum = pageCount;
//最后navPageCount页
for (int i = navigatePageCount - 1; i >= 0; i--) {
navigatePageNumbers[i] = endNum--;
}
} else {
//所有中间页
for (int i = 0; i < navigatePageCount; i++) {
navigatePageNumbers[i] = startNum++;
}
}
}
}
// endregion
}

View File

@@ -1,4 +1,4 @@
package cn.hutool.core.math;
package cn.hutool.core.lang.page;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.DefaultSegment;
@@ -36,7 +36,7 @@ public class PageInfo {
/**
* 总页数
*/
int pages;
int pageCount;
/**
* 首页标识
*/
@@ -74,7 +74,7 @@ public class PageInfo {
this.pageSize = pageSize;
// 因为总条数除以页大小的最大余数是页大小数-1
// 因此加一个最大余数保证舍弃的余数与最大余数凑1.x就是一旦有余数则+1页
this.pages = (total + pageSize - 1) / pageSize;
this.pageCount = (total + pageSize - 1) / pageSize;
}
/**
@@ -100,8 +100,8 @@ public class PageInfo {
*
* @return {int}
*/
public int getPages() {
return pages;
public int getPageCount() {
return pageCount;
}
/**
@@ -129,7 +129,7 @@ public class PageInfo {
* @return 是否尾页
*/
public boolean isLastPage() {
return getPageIndexBase1() == this.pages;
return getPageIndexBase1() == this.pageCount;
}
/**
@@ -147,7 +147,7 @@ public class PageInfo {
* @return 是否有下一页
*/
public boolean hasNextPage() {
return getBeginIndex() < this.pages;
return getBeginIndex() < this.pageCount;
}
/**
@@ -155,7 +155,7 @@ public class PageInfo {
* @return 是否可用
*/
public boolean isValidPage(){
return this.getPageIndexBase1() <= this.pages;
return this.getPageIndexBase1() <= this.pageCount;
}
/**
@@ -227,7 +227,8 @@ public class PageInfo {
/**
* 设置首页编号即以数字几为第一页标志<br>
* 如设置0则0表示第一页1表示第二页
* 如设置0则0表示第一页1表示第二页<br>
* 设置此参数后须调用{@link #setPageNo(int)} 重新设置当前页的页码
*
* @param firstPageNo 首页编号
* @return this
@@ -238,7 +239,9 @@ public class PageInfo {
}
/**
* 设置当前页码
* 设置当前页码具体这个页码代表实际页取决于{@link #setFirstPageNo(int)}设置的值
* 例如当{@link #setFirstPageNo(int)}设置为1时1表示首页设置为0时0表示首页依次类推<br>
* 当设置页码小于{@link #getFirstPageNo()}值时始终为{@link #getFirstPageNo()}
*
* @param pageNo 当前页码
* @return this
@@ -251,7 +254,8 @@ public class PageInfo {
}
/**
* 下一页即当前页码+1
* 下一页即当前页码+1<br>
* 当超过末页时此方法指向的页码值始终为{@link #getPageCount()} + 1即最后一页后的空白页
*
* @return this
*/
@@ -264,14 +268,14 @@ public class PageInfo {
*
* @return this
*/
public PageInfo PreviousPage() {
public PageInfo previousPage() {
return setPageNo(this.pageNo - 1);
}
public String toString() {
return "{" +
"total=" + total +
",pages=" + pages +
",pages=" + pageCount +
",pageNumber=" + pageNo +
",limit=" + pageSize +
",isFirstPage=" + isFirstPage() +

View File

@@ -0,0 +1,10 @@
/**
* 提供分页信息封装,主要包括:
* <ul>
* <li>{@link cn.hutool.core.lang.page.PageInfo}: 提供分页信息。</li>
* <li>{@link cn.hutool.core.lang.page.NavigatePageInfo}: 提供分页导航信息。</li>
* </ul>
*
* @author looly
*/
package cn.hutool.core.lang.page;

View File

@@ -1,71 +0,0 @@
package cn.hutool.core.math;
public class NavigatePageInfo extends PageInfo{
private final int navigatePages = 8; //导航页码数
private int[] navigatePageNumbers; //所有导航页号
public NavigatePageInfo(final int total, final int pageSize) {
super(total, pageSize);
//基本参数设定之后进行导航页面的计算
calcNavigatePageNumbers();
}
/**
* 得到所有导航页号
*
* @return {int[]}
*/
public int[] getNavigatePageNumbers() {
return navigatePageNumbers;
}
public String toString() {
final StringBuilder str = new StringBuilder(super.toString());
str.append(", {navigatePageNumbers=");
final int len = navigatePageNumbers.length;
if (len > 0) str.append(navigatePageNumbers[0]);
for (int i = 1; i < len; i++) {
str.append(" ").append(navigatePageNumbers[i]);
}
str.append("}");
return str.toString();
}
/**
* 计算导航页
*/
private void calcNavigatePageNumbers() {
//当总页数小于或等于导航页码数时
if (pages <= navigatePages) {
navigatePageNumbers = new int[pages];
for (int i = 0; i < pages; i++) {
navigatePageNumbers[i] = i + 1;
}
} else { //当总页数大于导航页码数时
navigatePageNumbers = new int[navigatePages];
int startNum = pageNo - navigatePages / 2;
int endNum = pageNo + navigatePages / 2;
if (startNum < 1) {
startNum = 1;
//(最前navPageCount页
for (int i = 0; i < navigatePages; i++) {
navigatePageNumbers[i] = startNum++;
}
} else if (endNum > pages) {
endNum = pages;
//最后navPageCount页
for (int i = navigatePages - 1; i >= 0; i--) {
navigatePageNumbers[i] = endNum--;
}
} else {
//所有中间页
for (int i = 0; i < navigatePages; i++) {
navigatePageNumbers[i] = startNum++;
}
}
}
}
}

View File

@@ -9,7 +9,7 @@ import java.util.*;
*
* @author renyp
*/
public class Automaton {
public class NFA {
/**
* AC树的根节点
*/
@@ -17,20 +17,26 @@ public class Automaton {
/**
* 标记是否需要构建AC自动机做树优化
*/
private volatile boolean needBuildAC;
private volatile boolean needBuildAc;
/**
* 内置锁防止并发场景并行建AC树造成不可预知结果
*/
private final Object lock;
private final Object buildAcLock;
/**
* 内置锁防止并行插入新节点建立后被挂载到树上前 被篡改
*/
private final Object insertTreeLock;
/**
* 默认构造
*/
public Automaton() {
public NFA() {
this.root = new Node();
this.needBuildAC = true;
this.lock = new Object();
this.needBuildAc = true;
this.buildAcLock = new Object();
this.insertTreeLock = new Object();
}
/**
@@ -38,7 +44,7 @@ public class Automaton {
*
* @param words 添加的新词
*/
public Automaton(String... words) {
public NFA(String... words) {
this();
this.insert(words);
}
@@ -49,17 +55,19 @@ public class Automaton {
* @param word 添加的新词
*/
public void insert(String word) {
needBuildAC = true;
Node p = root;
for (char curr : word.toCharArray()) {
int ind = curr;
if (p.next.get(ind) == null) {
p.next.put(ind, new Node());
synchronized (insertTreeLock) {
needBuildAc = true;
Node p = root;
for (char curr : word.toCharArray()) {
int ind = curr;
if (p.next.get(ind) == null) {
p.next.put(ind, new Node());
}
p = p.next.get(ind);
}
p = p.next.get(ind);
p.flag = true;
p.str = word;
}
p.flag = true;
p.str = word;
}
/**
@@ -101,7 +109,7 @@ public class Automaton {
queue.offer(curr.next.get(key));
}
}
needBuildAC = false;
needBuildAc = false;
}
/**
@@ -117,9 +125,9 @@ public class Automaton {
*/
public List<FoundWord> find(String text, boolean isDensityMatch) {
// double check防止重复无用的 buildAC
if (needBuildAC) {
synchronized (lock) {
if (needBuildAC) {
if (needBuildAc) {
synchronized (buildAcLock) {
if (needBuildAc) {
this.buildAc();
}
}
@@ -153,6 +161,7 @@ public class Automaton {
return ans;
}
private static class Node {
/**

View File

@@ -2,7 +2,7 @@ package cn.hutool.core.collection;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Console;
import cn.hutool.core.math.PageInfo;
import cn.hutool.core.lang.page.PageInfo;
import cn.hutool.core.util.RandomUtil;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@@ -12,11 +12,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.lang.invoke.LambdaConversionException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
@@ -24,6 +21,7 @@ import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author nasodaengineer
@@ -160,6 +158,15 @@ public class LambdaFactoryTest {
loop(count, tasks);
}
@Test
public void testConstructor() {
Constructor<Something> constructor = ((SerSupplier<Constructor<Something>>) Something.class::getConstructor).get();
Supplier<Something> constructorLambda = LambdaFactory.build(Supplier.class, constructor);
// constructorLambda can be cache or transfer
Something something = constructorLambda.get();
Assert.assertEquals(Something.class, something.getClass());
}
/**
* <p>hardCode 运行1次耗时 7600 NANOSECONDS
* <p>lambda 运行1次耗时 12400 NANOSECONDS

View File

@@ -0,0 +1,37 @@
package cn.hutool.core.lang.page;
import org.junit.Assert;
import org.junit.Test;
public class NavigatePageInfoTest {
@Test
public void naviTest1(){
// 首页
final NavigatePageInfo navigatePageInfo = new NavigatePageInfo(10, 2, 6);
Assert.assertEquals("[1] 2 3 4 5 >>", navigatePageInfo.toString());
// 中间页
navigatePageInfo.nextPage();
Assert.assertEquals("<< 1 [2] 3 4 5 >>", navigatePageInfo.toString());
// 尾页
navigatePageInfo.setPageNo(5);
Assert.assertEquals("<< 1 2 3 4 [5]", navigatePageInfo.toString());
}
@Test
public void naviTest2(){
// 首页
final NavigatePageInfo navigatePageInfo = new NavigatePageInfo(10, 2, 4);
Assert.assertEquals("[1] 2 3 4 >>", navigatePageInfo.toString());
// 中间页
navigatePageInfo.nextPage();
Assert.assertEquals("<< 1 [2] 3 4 >>", navigatePageInfo.toString());
// 尾页
navigatePageInfo.setPageNo(5);
Assert.assertEquals("<< 2 3 4 [5]", navigatePageInfo.toString());
}
}

View File

@@ -1,4 +1,4 @@
package cn.hutool.core.math;
package cn.hutool.core.lang.page;
import org.junit.Assert;
import org.junit.Test;
@@ -7,10 +7,10 @@ public class PageInfoTest {
@Test
public void pagesTest() {
PageInfo pageInfo = new PageInfo(20, 3);
Assert.assertEquals(7, pageInfo.getPages());
Assert.assertEquals(7, pageInfo.getPageCount());
pageInfo = new PageInfo(20, 4);
Assert.assertEquals(5, pageInfo.getPages());
Assert.assertEquals(5, pageInfo.getPageCount());
}
@Test

View File

@@ -1,225 +0,0 @@
package cn.hutool.core.text.dfa;
import cn.hutool.core.date.StopWatch;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
public class AutomatonTest extends TestCase {
/**
* 密集匹配 测试查找结果并与WordTree对比效率
*/
public void testFind() {
Automaton automaton = new Automaton();
WordTree wordTree = new WordTree();
automaton.insert("say", "her", "he", "she", "shr");
// automaton.buildAc();
wordTree.addWords("say", "her", "he", "she", "shr");
StopWatch stopWatch = new StopWatch();
String input = "sasherhsay";
stopWatch.start("automaton_char_find");
List<FoundWord> ans1 = automaton.find(input);
stopWatch.stop();
assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
assertEquals(Integer.valueOf(3), ans1.get(1).getStartIndex());
assertEquals(Integer.valueOf(4), ans1.get(1).getEndIndex());
assertEquals(Integer.valueOf(3), ans1.get(2).getStartIndex());
assertEquals(Integer.valueOf(5), ans1.get(2).getEndIndex());
assertEquals(Integer.valueOf(7), ans1.get(3).getStartIndex());
assertEquals(Integer.valueOf(9), ans1.get(3).getEndIndex());
stopWatch.start("wordtree_char_find");
List<String> ans2 = wordTree.matchAll(input, -1, true, true);
stopWatch.stop();
assertEquals("she,he,her,say", String.join(",", ans2));
System.out.println(stopWatch.prettyPrint());
}
/**
* 非密集匹配 测试查找结果并与WordTree对比效率
*/
public void testFindNotDensity() {
Automaton automaton = new Automaton();
WordTree wordTree = new WordTree();
automaton.insert("say", "her", "he", "she", "shr");
// automaton.buildAc();
wordTree.addWords("say", "her", "he", "she", "shr");
StopWatch stopWatch = new StopWatch();
String input = "sasherhsay";
stopWatch.start("automaton_char_find_not_density");
List<FoundWord> ans1 = automaton.find(input, false);
stopWatch.stop();
assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
assertEquals(Integer.valueOf(7), ans1.get(1).getStartIndex());
assertEquals(Integer.valueOf(9), ans1.get(1).getEndIndex());
stopWatch.start("wordtree_char_find_not_density");
List<String> ans2 = wordTree.matchAll(input, -1, false, true);
stopWatch.stop();
assertEquals("she,say", String.join(",", ans2));
System.out.println(stopWatch.prettyPrint());
}
/**
* 密集匹配 测试建树和查找并与WordTree对比效率
*/
public void testBuildAndFind() {
StopWatch stopWatch = new StopWatch();
String input = "sasherhsay";
stopWatch.start("automaton_char_buid_find");
Automaton automatonLocal = new Automaton();
automatonLocal.insert("say", "her", "he", "she", "shr");
// automatonLocal.buildAc();
List<FoundWord> ans1 = automatonLocal.find(input);
stopWatch.stop();
assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
assertEquals(Integer.valueOf(3), ans1.get(1).getStartIndex());
assertEquals(Integer.valueOf(4), ans1.get(1).getEndIndex());
assertEquals(Integer.valueOf(3), ans1.get(2).getStartIndex());
assertEquals(Integer.valueOf(5), ans1.get(2).getEndIndex());
assertEquals(Integer.valueOf(7), ans1.get(3).getStartIndex());
assertEquals(Integer.valueOf(9), ans1.get(3).getEndIndex());
stopWatch.start("wordtree_char_build_find");
WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("say", "her", "he", "she", "shr");
List<String> ans2 = wordTreeLocal.matchAll(input, -1, true, true);
stopWatch.stop();
assertEquals("she,he,her,say", String.join(",", ans2));
System.out.println(stopWatch.prettyPrint());
}
/**
* 密集匹配 构建树和查找 测试中文字符并与wordTree对比效率
*/
@Test
public void testBuildFindCnChar() {
StopWatch stopWatch = new StopWatch();
String input = "赵啊三在做什么";
stopWatch.start("automaton_cn_build_find");
Automaton automatonLocal = new Automaton();
automatonLocal.insert("", "赵啊", "赵啊三");
// automatonLocal.buildAc();
final List<FoundWord> result = automatonLocal.find(input);
stopWatch.stop();
Assert.assertEquals(3, result.size());
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
assertEquals(Integer.valueOf(0), result.get(1).getStartIndex());
assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
assertEquals(Integer.valueOf(0), result.get(2).getStartIndex());
assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
stopWatch.start("wordtree_cn_build_find");
WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
final List<String> result1 = wordTreeLocal.matchAll(input, -1, true, true);
stopWatch.stop();
Assert.assertEquals(3, result1.size());
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
System.out.println(stopWatch.prettyPrint());
}
/**
* 密集匹配 测试构建树和查找 中文字符并与wordTree对比效率
*/
@Test
public void testFindCNChar() {
StopWatch stopWatch = new StopWatch();
String input = "赵啊三在做什么";
Automaton automatonLocal = new Automaton();
automatonLocal.insert("", "赵啊", "赵啊三");
// automatonLocal.buildAc();
stopWatch.start("automaton_cn_find");
final List<FoundWord> result = automatonLocal.find(input);
stopWatch.stop();
Assert.assertEquals(3, result.size());
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
assertEquals(Integer.valueOf(0), result.get(1).getStartIndex());
assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
assertEquals(Integer.valueOf(0), result.get(2).getStartIndex());
assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
stopWatch.start("wordtree_cn_find");
final List<String> result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord)
.collect(Collectors.toList());
stopWatch.stop();
Assert.assertEquals(3, result1.size());
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
System.out.println(stopWatch.prettyPrint());
}
/**
* 非密集匹配 测试构建树和查找 中文字符并与wordTree对比效率
*/
@Test
public void testFindCNCharNotDensity() {
StopWatch stopWatch = new StopWatch();
String input = "赵啊三在做什么";
Automaton automatonLocal = new Automaton();
automatonLocal.insert("", "赵啊", "赵啊三");
// automatonLocal.buildAc();
stopWatch.start("automaton_cn_find_not_density");
final List<FoundWord> result = automatonLocal.find(input, false);
stopWatch.stop();
Assert.assertEquals(1, result.size());
Assert.assertEquals("", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
stopWatch.start("wordtree_cn_find_not_density");
final List<String> result1 =
wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord)
.collect(Collectors.toList());
stopWatch.stop();
Assert.assertEquals(1, result1.size());
Assert.assertEquals("", String.join(",", result1));
System.out.println(stopWatch.prettyPrint());
}
}

View File

@@ -0,0 +1,228 @@
package cn.hutool.core.text.dfa;
import cn.hutool.core.date.StopWatch;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
public class NFATest {
/**
* 密集匹配 测试查找结果并与WordTree对比效率
*/
@Test
public void testFind() {
final NFA NFA = new NFA();
NFA.insert("say", "her", "he", "she", "shr");
// NFA.buildAc();
final WordTree wordTree = new WordTree();
wordTree.addWords("say", "her", "he", "she", "shr");
final StopWatch stopWatch = new StopWatch();
final String input = "sasherhsay";
stopWatch.start("automaton_char_find");
final List<FoundWord> ans1 = NFA.find(input);
stopWatch.stop();
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
stopWatch.start("wordtree_char_find");
final List<String> ans2 = wordTree.matchAll(input, -1, true, true);
stopWatch.stop();
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
//Console.log(stopWatch.prettyPrint());
}
/**
* 非密集匹配 测试查找结果并与WordTree对比效率
*/
@Test
public void testFindNotDensity() {
final NFA NFA = new NFA();
NFA.insert("say", "her", "he", "she", "shr");
// NFA.buildAc();
final WordTree wordTree = new WordTree();
wordTree.addWords("say", "her", "he", "she", "shr");
final StopWatch stopWatch = new StopWatch();
final String input = "sasherhsay";
stopWatch.start("automaton_char_find_not_density");
final List<FoundWord> ans1 = NFA.find(input, false);
stopWatch.stop();
Assert.assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
Assert.assertEquals(7, ans1.get(1).getBeginIndex().intValue());
Assert.assertEquals(9, ans1.get(1).getEndIndex().intValue());
stopWatch.start("wordtree_char_find_not_density");
final List<String> ans2 = wordTree.matchAll(input, -1, false, true);
stopWatch.stop();
Assert.assertEquals("she,say", String.join(",", ans2));
//Console.log(stopWatch.prettyPrint());
}
/**
* 密集匹配 测试建树和查找并与WordTree对比效率
*/
@Test
public void testBuildAndFind() {
final StopWatch stopWatch = new StopWatch();
final String input = "sasherhsay";
stopWatch.start("automaton_char_buid_find");
final NFA NFALocal = new NFA();
NFALocal.insert("say", "her", "he", "she", "shr");
// NFALocal.buildAc();
final List<FoundWord> ans1 = NFALocal.find(input);
stopWatch.stop();
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
stopWatch.start("wordtree_char_build_find");
final WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("say", "her", "he", "she", "shr");
final List<String> ans2 = wordTreeLocal.matchAll(input, -1, true, true);
stopWatch.stop();
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
//Console.log(stopWatch.prettyPrint());
}
/**
* 密集匹配 构建树和查找 测试中文字符并与wordTree对比效率
*/
@Test
public void buildFindCnCharTest() {
final StopWatch stopWatch = new StopWatch();
final String input = "赵啊三在做什么";
stopWatch.start("automaton_cn_build_find");
final NFA NFALocal = new NFA();
NFALocal.insert("", "赵啊", "赵啊三");
// NFALocal.buildAc();
final List<FoundWord> result = NFALocal.find(input);
stopWatch.stop();
Assert.assertEquals(3, result.size());
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
stopWatch.start("wordtree_cn_build_find");
final WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
final List<String> result1 = wordTreeLocal.matchAll(input, -1, true, true);
stopWatch.stop();
Assert.assertEquals(3, result1.size());
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
//Console.log(stopWatch.prettyPrint());
}
/**
* 密集匹配 测试构建树和查找 中文字符并与wordTree对比效率
*/
@Test
public void testFindCNChar() {
final StopWatch stopWatch = new StopWatch();
final String input = "赵啊三在做什么";
final NFA NFALocal = new NFA();
NFALocal.insert("", "赵啊", "赵啊三");
// NFALocal.buildAc();
stopWatch.start("automaton_cn_find");
final List<FoundWord> result = NFALocal.find(input);
stopWatch.stop();
Assert.assertEquals(3, result.size());
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
final WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
stopWatch.start("wordtree_cn_find");
final List<String> result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord)
.collect(Collectors.toList());
stopWatch.stop();
Assert.assertEquals(3, result1.size());
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
//Console.log(stopWatch.prettyPrint());
}
/**
* 非密集匹配 测试构建树和查找 中文字符并与wordTree对比效率
*/
@Test
public void testFindCNCharNotDensity() {
final StopWatch stopWatch = new StopWatch();
final String input = "赵啊三在做什么";
final NFA NFALocal = new NFA();
NFALocal.insert("", "赵啊", "赵啊三");
// NFALocal.buildAc();
stopWatch.start("automaton_cn_find_not_density");
final List<FoundWord> result = NFALocal.find(input, false);
stopWatch.stop();
Assert.assertEquals(1, result.size());
Assert.assertEquals("", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
final WordTree wordTreeLocal = new WordTree();
wordTreeLocal.addWords("", "赵啊", "赵啊三");
stopWatch.start("wordtree_cn_find_not_density");
final List<String> result1 =
wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord)
.collect(Collectors.toList());
stopWatch.stop();
Assert.assertEquals(1, result1.size());
Assert.assertEquals("", String.join(",", result1));
//Console.log(stopWatch.prettyPrint());
}
}

View File

@@ -0,0 +1,48 @@
package cn.hutool.core.tree;
import cn.hutool.core.lang.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class IssueI6NR2ZTest {
@Test
public void getNodeTest() {
final List<TreeNode<Integer>> list = new ArrayList<>();
final TreeNode<Integer> treeNode1 = new TreeNode<>();
treeNode1.setId(1);
treeNode1.setParentId(0);
list.add(treeNode1);
final TreeNode<Integer> treeNode2 = new TreeNode<>();
treeNode2.setId(2);
treeNode2.setParentId(1);
list.add(treeNode2);
final TreeNode<Integer> treeNode3 = new TreeNode<>();
treeNode3.setId(3);
treeNode3.setParentId(1);
list.add(treeNode3);
final TreeNode<Integer> treeNode4 = new TreeNode<>();
treeNode4.setId(21);
treeNode4.setParentId(2);
list.add(treeNode4);
final TreeNode<Integer> treeNode5 = new TreeNode<>();
treeNode5.setId(31);
treeNode5.setParentId(3);
list.add(treeNode5);
final TreeNode<Integer> treeNode6 = new TreeNode<>();
treeNode6.setId(211);
treeNode6.setParentId(21);
list.add(treeNode6);
final List<MapTree<Integer>> build = TreeUtil.build(list);
final MapTree<Integer> node = TreeUtil.getNode(build.get(0), 31);
Assert.notNull(node);
}
}