词库加载模块的源码:
Java开源分词系统IKAnalyzer学习(四) 词库加载源代码——Dictionary类
Java开源分词系统IKAnalyzer学习(五) 词库加载源代码——DictSegmenty类
Java开源分词系统IKAnalyzer学习(六) 词库加载源代码——Hit类
首先这个词典管理类Dictionary类采用的设计模式是单立模式,实现的代码:
/*
* 词典初始化
*/
static{
singleton = new Dictionary();
}
private Dictionary(){
//初始化系统词典
loadMainDict();
loadSurnameDict();
loadQuantifierDict();
loadSuffixDict();
loadPrepDict();
loadStopWordDict();
}
/**
* 词典初始化
* 由于IK Analyzer的词典采用Dictionary类的静态方法进行词典初始化
* 只有当Dictionary类被实际调用时,才会开始载入词典,
* 这将延长首次分词操作的时间
* 该方法提供了一个在应用加载阶段就初始化字典的手段
* 用来缩短首次分词时的时延
* @return Dictionary
*/
public static Dictionary getInstance(){
return Dictionary.singleton;
}
词库加载的关键代码,这里以主词典为例,其他大同小异
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is , "UTF-8"), 512);
String theWord = null;
do {
theWord = br.readLine();
//假如还没有读到文件尾
if (theWord != null && !"".equals(theWord.trim())) {
_MainDict.fillSegment(theWord.trim().toCharArray());
}
} while (theWord != null);
} catch (IOException ioe) {
System.err.println("Main Dictionary loading exception.");
ioe.printStackTrace();
}finally{
try {
if(is != null){
is.close();
is = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
底层的字典存储代码
/**
* 加载填充词典片段
* @param charArray
* @param begin
* @param length
*/
public synchronized void fillSegment(char[] charArray , int begin , int length){
//获取字典表中的汉字对象
Character beginChar = new Character(charArray[begin]);
Character keyChar = charMap.get(beginChar);
//字典中没有该字,则将其添加入字典
if(keyChar == null){
charMap.put(beginChar, beginChar);
keyChar = beginChar;
}
//搜索当前节点的存储,查询对应keyChar的keyChar,如果没有则创建(这段代码没看明白)
DictSegment ds = lookforSegment(keyChar);
//处理keyChar对应的segment
if(length > 1){
//词元还没有完全加入词典树
ds.fillSegment(charArray, begin + 1, length - 1);
}else if (length == 1){
//已经是词元的最后一个char,设置当前节点状态为1,表明一个完整的词
ds.nodeState = 1;
}
}
/**
* 查找本节点下对应的keyChar的segment
* 如果没有找到,则创建新的segment
* @param keyChar
* @return
*/
private DictSegment lookforSegment(Character keyChar){
DictSegment ds = null;
if(this.storeSize <= ARRAY_LENGTH_LIMIT){
//获取数组容器,如果数组未创建则创建数组
DictSegment[] segmentArray = getChildrenArray();
//搜寻数组
for(DictSegment segment : segmentArray){
if(segment != null && segment.nodeChar.equals(keyChar)){
//在数组中找到与keyChar对应的segment
ds = segment;
break;
}
}
//遍历数组后没有找到对应的segment
if(ds == null){
//构造新的segment
ds = new DictSegment(keyChar);
if(this.storeSize < ARRAY_LENGTH_LIMIT){
//数组容量未满,使用数组存储
segmentArray[this.storeSize] = ds;
//segment数目+1
this.storeSize++;
}else{
//数组容量已满,切换Map存储
//获取Map容器,如果Map未创建,则创建Map
Map<Character , DictSegment> segmentMap = getChildrenMap();
//将数组中的segment迁移到Map中
migrate(segmentArray , segmentMap);
//存储新的segment
segmentMap.put(keyChar, ds);
//segment数目+1 , 必须在释放数组前执行storeSize++ , 确保极端情况下,不会取到空的数组
this.storeSize++;
//释放当前的数组引用
this.childrenArray = null;
}
}
}else{
//获取Map容器,如果Map未创建,则创建Map
Map<Character , DictSegment> segmentMap = getChildrenMap();
//搜索Map
ds = (DictSegment)segmentMap.get(keyChar);
if(ds == null){
//构造新的segment
ds = new DictSegment(keyChar);
segmentMap.put(keyChar , ds);
//当前节点存储segment数目+1
this.storeSize ++;
}
}
return ds;
}