java實現簡單的搜索引擎

先寫具體的實現代碼,具體的實現思路和邏輯寫在代碼以後。java

搜索時用於排序的Bean算法

/**  
 *@Description:   
 */  
package cn.lulei.search.engine.model;  
  
public class SortBean { 
  private String id; 
  private int times; 
   
  public String getId() { 
    return id; 
  } 
  public void setId(String id) { 
    this.id = id; 
  } 
  public int getTimes() { 
    return times; 
  } 
  public void setTimes(int times) { 
    this.times = times; 
  } 
}

構造的搜索數據結構以及簡單的搜索算法數組

/**  
 *@Description:   
 */  
package cn.lulei.search.engine;  
 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.HashMap; 
import java.util.HashSet; 
import java.util.List; 
 
import cn.lulei.search.engine.model.SortBean; 
  
public class SerachBase { 
  //details 存儲搜素對象的詳細信息,其中key做爲區分Object的惟一標識 
  private HashMap<String, Object> details = new HashMap<String, Object>(); 
  //對於參與搜索的關鍵詞,這裏採用的稀疏數組存儲,也能夠採用HashMap來存儲,定義格式以下 
  //private static HashMap<Integer, HashSet<String>> keySearch = new HashMap<Integer, HashSet<String>>(); 
  //HashMap中額key值至關於稀疏數組中的下標,value至關於稀疏數組在該位置的值 
  private final static int maxLength = Character.MAX_VALUE; 
  @SuppressWarnings("unchecked") 
  private HashSet<String>[] keySearch = new HashSet[maxLength]; 
   
  /** 
   *@Description: 實現單例模式,採用Initialization on Demand Holder加載 
   *@Version:1.1.0 
   */ 
  private static class lazyLoadSerachBase { 
    private static final SerachBase serachBase = new SerachBase(); 
  } 
   
  /** 
   * 這裏把構造方法設置成私有爲的是單例模式 
   */ 
  private SerachBase() { 
     
  } 
   
  /** 
   * @return  
   * @Description: 獲取單例 
   */ 
  public static SerachBase getSerachBase() { 
    return lazyLoadSerachBase.serachBase; 
  } 
   
  /** 
   * @param id 
   * @return  
   * @Description: 根據id獲取詳細 
   */ 
  public Object getObject(String id) { 
    return details.get(id); 
  } 
   
  /** 
   * @param ids 
   * @return  
   * @Description: 根據ids獲取詳細,id之間用","隔開 
   */ 
  public List<Object> getObjects(String ids) { 
    if (ids == null || "".equals(ids)) { 
      return null; 
    } 
    List<Object> objs = new ArrayList<Object>(); 
    String[] idArray = ids.split(","); 
    for (String id : idArray) { 
      objs.add(getObject(id)); 
    } 
    return objs; 
  } 
   
  /** 
   * @param key 
   * @return  
   * @Description: 根據搜索詞查找對應的id,id之間用","分割 
   */ 
  public String getIds(String key) { 
    if (key == null || "".equals(key)) { 
      return null; 
    } 
    //查找 
    //idTimes存儲搜索詞每一個字符在id中是否出現 
    HashMap<String, Integer> idTimes = new HashMap<String, Integer>(); 
    //ids存儲出現搜索詞中的字符的id 
    HashSet<String> ids = new HashSet<String>(); 
     
    //從搜索庫中去查找 
    for (int i = 0; i < key.length(); i++) { 
      int at = key.charAt(i); 
      //搜索詞庫中沒有對應的字符,則進行下一個字符的匹配 
      if (keySearch[at] == null) { 
        continue; 
      } 
      for (Object obj : keySearch[at].toArray()) { 
        String id = (String) obj; 
        int times = 1; 
        if (ids.contains(id)) { 
          times += idTimes.get(id); 
          idTimes.put(id, times); 
        } else { 
          ids.add(id); 
          idTimes.put(id, times); 
        } 
      } 
    } 
     
    //使用數組排序 
    List<SortBean> sortBeans = new ArrayList<SortBean>(); 
    for (String id : ids) { 
      SortBean sortBean = new SortBean(); 
      sortBeans.add(sortBean); 
      sortBean.setId(id); 
      sortBean.setTimes(idTimes.get(id)); 
    } 
    Collections.sort(sortBeans, new Comparator<SortBean>(){ 
      public int compare(SortBean o1, SortBean o2){ 
        return o2.getTimes() - o1.getTimes(); 
      } 
    }); 
     
    //構建返回字符串 
    StringBuffer sb = new StringBuffer(); 
    for (SortBean sortBean : sortBeans) { 
      sb.append(sortBean.getId()); 
      sb.append(","); 
    } 
     
    //釋放資源 
    idTimes.clear(); 
    idTimes = null; 
    ids.clear(); 
    ids = null; 
    sortBeans.clear(); 
    sortBeans = null; 
     
    //返回 
    return sb.toString(); 
  } 
   
  /** 
   * @param id 
   * @param searchKey 
   * @param obj 
   * @Description: 添加搜索記錄 
   */ 
  public void add(String id, String searchKey, Object obj) { 
    //參數有部分爲空,不加載 
    if (id == null || searchKey == null || obj == null) { 
      return; 
    } 
    //保存對象 
    details.put(id, obj); 
    //保存搜索詞 
    addSearchKey(id, searchKey); 
  } 
   
  /** 
   * @param id 
   * @param searchKey 
   * @Description: 將搜索詞加入到搜索域中 
   */ 
  private void addSearchKey(String id, String searchKey) { 
    //參數有部分爲空,不加載 
    //這裏是私有方法,能夠不作以下判斷,但爲了設計規範,仍是加上 
    if (id == null || searchKey == null) { 
      return; 
    } 
    //下面採用的是字符分詞,這裏也能夠使用如今成熟的其餘分詞器 
    for (int i = 0; i < searchKey.length(); i++) { 
      //at值至關因而數組的下標,id組成的HashSet至關於數組的值 
      int at = searchKey.charAt(i); 
      if (keySearch[at] == null) { 
        HashSet<String> value = new HashSet<String>(); 
        keySearch[at] = value; 
      } 
      keySearch[at].add(id); 
    } 
  } 
   
   
 
}

測試用例:數據結構

/**  
 *@Description:   
 */  
package cn.lulei.search.engine.test;  
 
import java.util.List; 
 
import cn.lulei.search.engine.SerachBase; 
  
public class Test { 
  public static void main(String[] args) { 
    // TODO Auto-generated method stub  
    SerachBase serachBase = SerachBase.getSerachBase(); 
    serachBase.add("1", "你好!", "你好!"); 
    serachBase.add("2", "你好!我是張三。", "你好!我是張三。"); 
    serachBase.add("3", "今天的天氣挺好的。", "今天的天氣挺好的。"); 
    serachBase.add("4", "你是誰?", "你是誰?"); 
    serachBase.add("5", "高數這門學科很難", "高數確實很難。"); 
    serachBase.add("6", "測試", "上面的只是測試"); 
    String ids = serachBase.getIds("你的高數"); 
    System.out.println(ids); 
    List<Object> objs = serachBase.getObjects(ids); 
    if (objs != null) { 
      for (Object obj : objs) { 
        System.out.println((String) obj); 
      } 
    } 
  } 
 
}

測試輸出結果以下:app

5,3,2,1,4,
高數確實很難。
今天的天氣挺好的。
你好!我是張三。
你好!
你是誰?

這樣一個簡單的搜索引擎也就算是完成了。學習

問題一:這裏面的分詞采用的是字符分詞,對漢語的處理仍是挺不錯的,可是對英文的處理就很弱。測試

改進方法:採用如今成熟的分詞方法,好比IKAnalyzer、StandardAnalyzer等,這樣修改,keySearch的數據結構就須要作下修改,能夠修改成 private HashMap<String, String>[] keySearch = new HashMap[maxLength]; 其中key存儲分的詞元,value存儲惟一標識id。this

問題二:本文實現的搜索引擎對詞元並無像lucene設置權重,只是簡單的判斷詞元是否在對象中出現。搜索引擎

改進方法:暫無。添加權重處理,使數據結構更加複雜,因此暫時沒有對其作處理,在從此的文章中會實現權重的處理。設計

下面就簡單的介紹一下搜索引擎的實現思路

在SerachBase類中設置details和keySearch兩個屬性,details用於存儲Object的詳細信息,keySearch用於對搜索域作索引。details數據格式爲HashMap,keySearch的數據格式爲稀疏數組(也能夠爲HashMap,HashMap中額key值至關於稀疏數組中的下標,value至關於稀疏數組在該位置的值)。

對於details我就不作太多的介紹。

keySearch中數組下標(如用HashMap就是key)的計算方法是獲取詞元的第一個字符int值(由於本文的分詞采用的是字符分詞,因此一個字符就是一個詞元),該int值就是數組的下標,相應的數組值就是Object的惟一標識。這樣keySearch的數據結構就以下圖

所以想添加新紀錄的時候只須要調用add方法便可。

對於搜索的實現邏輯和上面的keySearch相似。對於id的搜索直接使用HashMap的get方法便可。對於搜索詞的一個搜索,總體的過程也是採用先分詞、其次查詢、最後排序。固然這裏面的分詞要和建立採用的分詞要一致(即建立的時候採用字符分詞,查找的時候也採用字符分詞)。

在getIds方法中,HashMap<String, Integer> idTimes = new HashMap<String, Integer>();idTimes 變量用來存儲搜索詞中的詞元有多少個在keySearch中出現,key值爲惟一標識id,value爲出現的詞元個數。HashSet<String> ids = new HashSet<String>(); ids變量用來存儲出現的詞元的ids。這樣搜索的複雜度就是搜索詞的詞元個數n。得到包含詞元的ids,構造SortBean數組,對其排序,排序規則是出現詞元個數的降序排列。最後返回ids字符串,每一個id用","分割。如要獲取詳細信息
再使用getObjects方法便可。

上述的只是一個簡單的搜索引擎,並無設計太多的計算方法,但願對你們的學習有所啓發。

相關文章
相關標籤/搜索