词库加载模块的源码:
Java开源分词系统IKAnalyzer学习(四) 词库加载源代码——Dictionary类
Java开源分词系统IKAnalyzer学习(五) 词库加载源代码——DictSegmenty类
Java开源分词系统IKAnalyzer学习(六) 词库加载源代码——Hit类
首先这个词典管理类Dictionary类采用的设计模式是单立模式,实现的代码:
/* * 词典单子实例 */ private static final Dictionary singleton; /* * 词典初始化 */ 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; }
词库加载的关键代码,这里以主词典为例,其他大同小异
/** * 加载主词典及扩展词典 */ private void loadMainDict(){ //建立一个主词典实例 _MainDict = new DictSegment((char)0); //读取主词典文件 InputStream is = Dictionary.class.getResourceAsStream(Dictionary.PATH_DIC_MAIN); if(is == null){ throw new RuntimeException("Main Dictionary not found!!!"); } 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 */ public void fillSegment(char[] charArray){ this.fillSegment(charArray, 0 , charArray.length); } /** * 加载填充词典片段 * @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; }