6.7.0 - Alpha6 - Fine tuning

This commit is contained in:
SuperMonster003
2025-10-01 00:13:00 +08:00
parent 4fe314d301
commit f3e04b9ca2
165 changed files with 2356 additions and 2413 deletions

View File

@@ -4,7 +4,7 @@ import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* 词典树分段表示词典树的一个分枝
* 词典树分段, 表示词典树的一个分枝
*/
internal class DictSegment(
// 当前节点上存储的字符
@@ -35,25 +35,25 @@ internal class DictSegment(
fun match(charArray: CharArray, begin: Int = 0, length: Int = charArray.size, searchHit: Hit? = null): Hit {
var niceSearchHit = searchHit
if (niceSearchHit == null) {
// 如果hit为空新建
// 如果 hit 为空, 新建
niceSearchHit = Hit()
// 设置hit的其实文本位置
// 设置 hit 的其实文本位置
niceSearchHit.begin = begin
} else {
// 否则要将HIT状态重置
// 否则要将 HIT 状态重置
niceSearchHit.setUnmatch()
}
// 设置hit的当前处理位置
// 设置 hit 的当前处理位置
niceSearchHit.end = begin
val keyChar = charArray[begin]
var ds: DictSegment? = null
// 引用实例变量为本地变量避免查询时遇到更新的同步问题
// 引用实例变量为本地变量, 避免查询时遇到更新的同步问题
val segmentArray = this.childrenArray
val segmentMap: Map<Char, DictSegment> = this.childrenSegment
// STEP1 在节点中查找keyChar对应的DictSegment
// STEP1 在节点中查找 keyChar 对应的 DictSegment
if (segmentArray != null) {
// 在数组中查找
val keySegment = DictSegment(keyChar)
@@ -62,32 +62,32 @@ internal class DictSegment(
ds = segmentArray[position]
}
} else {
// 在map中查找
// 在 map 中查找
ds = segmentMap[keyChar]
}
// STEP2 找到DictSegment判断词的匹配状态是否继续递归还是返回结果
// STEP2 找到 DictSegment, 判断词的匹配状态, 是否继续递归, 还是返回结果
if (ds != null) {
if (length > 1) {
// 词未匹配完继续往下搜索
// 词未匹配完, 继续往下搜索
return ds.match(charArray, begin + 1, length - 1, niceSearchHit)
} else if (length == 1) {
// 搜索最后一个char
// 搜索最后一个 char
if (ds.nodeState == 1) {
// 添加HIT状态为完全匹配
// 添加 HIT 状态为完全匹配
niceSearchHit.setMatch()
}
if (ds.hasNextNode()) {
// 添加HIT状态为前缀匹配
// 添加 HIT 状态为前缀匹配
niceSearchHit.setPrefix()
// 记录当前位置的DictSegment
// 记录当前位置的 DictSegment
niceSearchHit.matchedDictSegment = ds
}
return niceSearchHit
}
}
// STEP3 没有找到DictSegment 将HIT设置为不匹配
// STEP3 没有找到 DictSegment, HIT 设置为不匹配
return niceSearchHit
}
@@ -114,40 +114,40 @@ internal class DictSegment(
// 获取字典表中的汉字对象
val beginChar = charArray[begin]
var keyChar = charMap[beginChar]
// 字典中没有该字则将其添加入字典
// 字典中没有该字, 则将其添加入字典
if (keyChar == null) {
charMap[beginChar] = beginChar
keyChar = beginChar
}
// 搜索当前节点的存储查询对应keyCharkeyChar如果没有则创建
// 搜索当前节点的存储, 查询对应 keyCharkeyChar, 如果没有则创建
val ds = lookforSegment(keyChar, enabled)
if (ds != null) {
// 处理keyChar对应的segment
// 处理 keyChar 对应的 segment
if (length > 1) {
// 词元还没有完全加入词典树
ds.fillSegment(charArray, begin + 1, length - 1, enabled)
} else if (length == 1) {
// 已经是词元的最后一个char,设置当前节点状态为enabled
// enabled=1表明一个完整的词enabled=0表示从词典中屏蔽当前词
// 已经是词元的最后一个 char, 设置当前节点状态为 enabled,
// enabled=1 表明一个完整的词, enabled=0 表示从词典中屏蔽当前词
ds.nodeState = enabled
}
}
}
/**
* 查找本节点下对应的keyCharsegment *
* 查找本节点下对应的 keyCharsegment *
*
* @param keyChar
* @param create
* =1如果没有找到则创建新的segment ; =0如果没有找到不创建返回null
* =1 如果没有找到, 则创建新的 segment ; =0 如果没有找到, 不创建, 返回 null
* @return
*/
private fun lookforSegment(keyChar: Char, create: Int): DictSegment? {
var ds: DictSegment? = null
if (this.storeSize <= ARRAY_LENGTH_LIMIT) {
// 获取数组容器如果数组未创建则创建数组
// 获取数组容器, 如果数组未创建则创建数组
val segmentArray = getChildrenArray()
// 搜寻数组
val keySegment = DictSegment(keyChar)
@@ -156,39 +156,39 @@ internal class DictSegment(
ds = segmentArray[position]
}
// 遍历数组后没有找到对应的segment
// 遍历数组后没有找到对应的 segment
if (ds == null && create == 1) {
ds = keySegment
if (this.storeSize < ARRAY_LENGTH_LIMIT) {
// 数组容量未满使用数组存储
// 数组容量未满, 使用数组存储
segmentArray[storeSize] = ds
// segment数目+1
// segment 数目 +1
storeSize++
Arrays.sort(segmentArray, 0, this.storeSize)
} else {
// 数组容量已满切换Map存储
// 获取Map容器如果Map未创建,则创建Map
// 数组容量已满, 切换 Map 存储
// 获取 Map 容器, 如果 Map 未创建, 则创建 Map
val segmentMap = getChildrenMap()
// 将数组中的segment迁移到Map中
// 将数组中的 segment 迁移到 Map
migrate(segmentArray, segmentMap)
// 存储新的segment
// 存储新的 segment
segmentMap[keyChar] = ds
// segment数目+1 必须在释放数组前执行storeSize++ 确保极端情况下不会取到空的数组
// segment 数目 +1, 必须在释放数组前执行 storeSize++, 确保极端情况下, 不会取到空的数组
storeSize++
// 释放当前的数组引用
this.childrenArray = null
}
}
} else {
// 获取Map容器如果Map未创建,则创建Map
// 获取 Map 容器, 如果 Map 未创建, 则创建 Map
val segmentMap = getChildrenMap()
// 搜索Map
// 搜索 Map
ds = segmentMap[keyChar]
if (ds == null && create == 1) {
// 构造新的segment
// 构造新的 segment
ds = DictSegment(keyChar)
segmentMap[keyChar] = ds
// 当前节点存储segment数目+1
// 当前节点存储 segment 数目 +1
storeSize++
}
}
@@ -215,7 +215,7 @@ internal class DictSegment(
}
/**
* 将数组中的segment迁移到Map中以支持高性能存储
* 将数组中的 segment 迁移到 Map 中以支持高性能存储
*/
private fun migrate(segmentArray: Array<DictSegment?>, segmentMap: MutableMap<Char, DictSegment>) {
for (segment in segmentArray) {
@@ -226,10 +226,10 @@ internal class DictSegment(
}
/**
* 实现Comparable接口
* 实现 Comparable 接口
*/
override fun compareTo(other: DictSegment): Int {
// 对当前节点存储的char进行比较
// 对当前节点存储的 char 进行比较
return nodeChar.compareTo(other.nodeChar)
}
@@ -240,11 +240,11 @@ internal class DictSegment(
}
companion object {
// 公用字典表存储汉字
// 公用字典表, 存储汉字
private val charMap: MutableMap<Char, Char> = HashMap(16, 0.95f)
// 数组大小上限
private const val ARRAY_LENGTH_LIMIT = 3
}
}
}

View File

@@ -34,14 +34,14 @@ abstract class DictionaryDatabase internal constructor(context: Context): Closea
val storedMd5 = prefs.getString(md5Key, null)
val currentMd5 by lazy { dbFile.md5() }
// 如果数据库文件存在检查 MD5
// 如果数据库文件存在, 检查 MD5
if (dbFile.exists()) {
if (storedMd5 != null && storedMd5 == currentMd5) {
if (!shouldForciblyCopyDatabase) return // 文件有效无需复制或解压
if (!shouldForciblyCopyDatabase) return // 文件有效, 无需复制或解压
}
}
// 数据文件不存在或 MD5 不匹配重新复制
// 数据文件不存在或 MD5 不匹配, 重新复制
dbFile.parentFile?.mkdirs()
context.assets.open(compressedDatabaseName).use { compressedInputStream ->
GZIPInputStream(compressedInputStream).use { gzipStream -> // 解压

View File

@@ -1,27 +1,27 @@
/**
*
*
* IK 中文分词 版本 5.0
* IK Analyzer release 5.0
*
* <p>
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 源代码由林良益(linliangyi2005@gmail.com)提供
* 版权声明 2012乌龙茶工作室
* <p>
* 源代码由林良益 (linliangyi2005@gmail.com) 提供
* 版权声明 2012, 乌龙茶工作室
* provided by Linliangyi and copyright 2012 by Oolong studio
*
*
*/
package com.huaban.analysis.jieba;
@@ -29,89 +29,93 @@ package com.huaban.analysis.jieba;
* 表示一次词典匹配的命中
*/
public class Hit {
//Hit不匹配
private static final int UNMATCH = 0x00000000;
//Hit完全匹配
private static final int MATCH = 0x00000001;
//Hit前缀匹配
private static final int PREFIX = 0x00000010;
//HIT当前状态默认未匹配
private int hitState = UNMATCH;
//记录词典匹配过程中当前匹配到的词典分支节点
private DictSegment matchedDictSegment;
/*
* 词段开始位置
*/
private int begin;
/*
* 词段的结束位置
*/
private int end;
/**
* 判断是否完全匹配
*/
public boolean isMatch() {
return (this.hitState & MATCH) > 0;
}
/**
*
*/
public void setMatch() {
this.hitState = this.hitState | MATCH;
}
// Hit 不匹配
private static final int UNMATCH = 0x00000000;
// Hit 完全匹配
private static final int MATCH = 0x00000001;
// Hit 前缀匹配
private static final int PREFIX = 0x00000010;
//HIT 当前状态, 默认未匹配
private int hitState = UNMATCH;
// 记录词典匹配过程中, 当前匹配到的词典分支节点
private DictSegment matchedDictSegment;
/*
* 词段开始位置
*/
private int begin;
/*
* 词段的结束位置
*/
private int end;
/**
* 判断是否完全匹配
*/
public boolean isMatch() {
return (this.hitState & MATCH) > 0;
}
/**
*
*/
public void setMatch() {
this.hitState = this.hitState | MATCH;
}
/**
* 判断是否是词的前缀
*/
public boolean isPrefix() {
return (this.hitState & PREFIX) > 0;
}
/**
*
*/
public void setPrefix() {
this.hitState = this.hitState | PREFIX;
}
/**
* 判断是否是不匹配
*/
public boolean isUnmatch() {
return this.hitState == UNMATCH;
}
/**
*
*/
public void setUnmatch() {
this.hitState = UNMATCH;
}
public DictSegment getMatchedDictSegment() {
return matchedDictSegment;
}
public void setMatchedDictSegment(DictSegment matchedDictSegment) {
this.matchedDictSegment = matchedDictSegment;
}
public int getBegin() {
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
/**
* 判断是否是词的前缀
*/
public boolean isPrefix() {
return (this.hitState & PREFIX) > 0;
}
/**
*
*/
public void setPrefix() {
this.hitState = this.hitState | PREFIX;
}
/**
* 判断是否是不匹配
*/
public boolean isUnmatch() {
return this.hitState == UNMATCH ;
}
/**
*
*/
public void setUnmatch() {
this.hitState = UNMATCH;
}
public DictSegment getMatchedDictSegment() {
return matchedDictSegment;
}
public void setMatchedDictSegment(DictSegment matchedDictSegment) {
this.matchedDictSegment = matchedDictSegment;
}
public int getBegin() {
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}