6.7.0 - Alpha6 - Fine tuning
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
// 搜索当前节点的存储,查询对应keyChar的keyChar,如果没有则创建
|
||||
// 搜索当前节点的存储, 查询对应 keyChar 的 keyChar, 如果没有则创建
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找本节点下对应的keyChar的segment *
|
||||
* 查找本节点下对应的 keyChar 的 segment *
|
||||
*
|
||||
* @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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -> // 解压
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user