<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 第八章 索引器 > 原文:[Chapter 8 Indexer](http://greenteapress.com/thinkdast/html/thinkdast009.html) > 譯者:[飛龍](https://github.com/wizardforcel) > 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) > 自豪地采用[谷歌翻譯](https://translate.google.cn/) 目前,我們構建了一個基本的 Web 爬蟲;我們下一步將是索引。在網頁搜索的上下文中,索引是一種數據結構,可以查找檢索詞并找到該詞出現的頁面。此外,我們想知道每個頁面上顯示檢索詞的次數,這將有助于確定與該詞最相關的頁面。 例如,如果用戶提交檢索詞“Java”和“編程”,我們將查找兩個檢索詞并獲得兩組頁面。帶有“Java”的頁面將包括 Java 島嶼,咖啡昵稱以及編程語言的網頁。具有“編程”一詞的頁面將包括不同編程語言的頁面,以及該單詞的其他用途。通過選擇具有兩個檢索詞的頁面,我們希望消除不相關的頁面,并找到 Java 編程的頁面。 現在我們了解索引是什么,它執行什么操作,我們可以設計一個數據結構來表示它。 ## 8.1 數據結構選取 索引的基本操作是查找;具體來說,我們需要能夠查找檢索詞并找到包含它的所有頁面。最簡單的實現將是頁面的集合。給定一個檢索詞,我們可以遍歷頁面的內容,并選擇包含檢索詞的內容。但運行時間與所有頁面上的總字數成正比,這太慢了。 一個更好的選擇是一個映射(字典),它是一個數據結構,表示鍵值對的集合,并提供了一種方法,快速查找鍵以及相應值。例如,我們將要構建的第一個映射是`TermCounter`,它將每個檢索詞映射為頁面中出現的次數。鍵是檢索詞,值是計數(也稱為“頻率”)。 Java 提供了`Map`的調用接口,它指定映射應該提供的方法;最重要的是: + `get(key)`:此方法查找一個鍵并返回相應的值。 + `put(key, value)`:該方法向`Map`添加一個新的鍵值對,或者如果該鍵已經在映射中,它將替換與`key`關聯的值。 Java 提供了幾個`Map`實現,包括我們將關注的兩個,`HashMap`以及`TreeMap`。在即將到來的章節中,我們將介紹這些實現并分析其性能。 除了檢索詞到計數的映射`TermCounter`之外,我們將定義一個被稱為`Index`的類,它將檢索詞映射為出現的頁面的集合。而這又引發了下一個問題,即如何表示頁面集合。同樣,如果我們考慮我們想要執行的操作,它們就指導了我們的決定。 在這種情況下,我們需要組合兩個或多個集合,并找到所有這些集合中顯示的頁面。你可以將此操作看做集合的交集:兩個集合的交集是出現在兩者中的一組元素。 你可能猜到了,Java 提供了一個`Set`接口,來定義集合應該執行的操作。它實際上并不提供設置交集,但它提供了方法,使我們能夠有??效地實現交集和其他結合操作。核心的`Set`方法是: + `add(element)`:該方法將一個元素添加到集合中;如果元素已經在集合中,則它不起作用。 + `contains(element)`:該方法檢查給定元素是否在集合中。 Java 提供了幾個`Set`實現,包括`HashSet`和`TreeSet`。 現在我們自頂向下設計了我們的數據結構,我們將從內到外實現它們,從`TermCounter`開始。 ## 8.2 `TermCounter` `TermCounter`是一個類,表示檢索詞到頁面中出現次數的映射。這是類定義的第一部分: ```java public class TermCounter { private Map<String, Integer> map; private String label; public TermCounter(String label) { this.label = label; this.map = new HashMap<String, Integer>(); } } ``` 實例變量`map`包含檢索詞到計數的映射,并且`label`標識檢索詞的來源文檔;我們將使用它來存儲 URL。 為了實現映射,我選擇了`HashMap`,它是最常用的`Map`。在幾章中,你將看到它是如何工作的,以及為什么它是一個常見的選擇。 `TermCounter`提供`put`和`get`,定義如下: ```java public void put(String term, int count) { map.put(term, count); } public Integer get(String term) { Integer count = map.get(term); return count == null ? 0 : count; } ``` `put`只是一個包裝方法;當你調用`TermCounter`的`put`時,它會調用內嵌映射的`put`。 另一方面,`get`做了一些實際工作。當你調用`TermCounter`的`get`時,它會在映射上調用`get`,然后檢查結果。如果該檢索詞沒有出現在映射中,則`TermCount.get`返回`0`。`get`的這種定義方式使`incrementTermCount`的寫入更容易,它需要一個檢索詞,并增加關聯該檢索詞的計數器。 ```java public void incrementTermCount(String term) { put(term, get(term) + 1); } ``` 如果這個檢索詞未見過,則`get`返回`0`;我們設為`1`,然后使用`put`向映射添加一個新的鍵值對。如果該檢索詞已經在映射中,我們得到舊的計數,增加`1`,然后存儲新的計數,替換舊的值。 此外,`TermCounter`還提供了這些其他方法,來幫助索引網頁: ```java public void processElements(Elements paragraphs) { for (Node node: paragraphs) { processTree(node); } } public void processTree(Node root) { for (Node node: new WikiNodeIterable(root)) { if (node instanceof TextNode) { processText(((TextNode) node).text()); } } } public void processText(String text) { String[] array = text.replaceAll("\\pP", " "). toLowerCase(). split("\\s+"); for (int i=0; i<array.length; i++) { String term = array[i]; incrementTermCount(term); } } ``` 最后,這里是一個例子,展示了如何使用`TermCounter`: ```java String url = "http://en.wikipedia.org/wiki/Java_(programming_language)"; WikiFetcher wf = new WikiFetcher(); Elements paragraphs = wf.fetchWikipedia(url); TermCounter counter = new TermCounter(url); counter.processElements(paragraphs); counter.printCounts(); ``` 這個示例使用了`WikiFetcher`從維基百科下載頁面,并解析正文。之后它創建了`TermCounter`并使用它來計數頁面上的單詞。 下一節中,你會擁有一個挑戰,來運行這個代碼,并通過填充缺失的方法來測試你的理解。 ## 8.3 練習 6 在本書的存儲庫中,你將找到此練習的源文件: + `TermCounter.java`包含上一節中的代碼。 + `TermCounterTest.java`包含測試代碼`TermCounter.java`。 + `Index.java`包含本練習下一部分的類定義。 + `WikiFetcher.java`包含我們在上一個練習中使用的,用于下載和解析網頁的類。 + `WikiNodeIterable.java`包含我們用于遍歷 DOM 樹中的節點的類。 你還會發現 Ant 構建文件`build.xml`。 運行`ant build`來編譯源文件。然后運行`ant TermCounter`;它應該運行上一節中的代碼,并打印一個檢索詞列表及其計數。輸出應該是這樣的: ``` genericservlet, 2 configurations, 1 claimed, 1 servletresponse, 2 occur, 2 Total of all counts = -1 ``` 運行它時,檢索詞的順序可能不同。 最后一行應該打印檢索詞計數的總和,但是由于方法`size`不完整而返回`-1`。填充此方法并`ant TermCounter`重新運行。結果應該是`4798`。 運行`ant TermCounterTest`來確認這部分練習是否完整和正確。 對于練習的第二部分,我將介紹`Index`對象的實現,你將填充一個缺失的方法。這是類定義的開始: ```java public class Index { private Map<String, Set<TermCounter>> index = new HashMap<String, Set<TermCounter>>(); public void add(String term, TermCounter tc) { Set<TermCounter> set = get(term); // if we're seeing a term for the first time, make a new Set if (set == null) { set = new HashSet<TermCounter>(); index.put(term, set); } // otherwise we can modify an existing Set set.add(tc); } public Set<TermCounter> get(String term) { return index.get(term); } ``` 實例變量`index`是每個檢索詞到一組`TermCounter`對象的映射。每個`TermCounter`表示檢索詞出現的頁面。 `add`方法向集合添加新的`TermCounter`,它與檢索詞關聯。當我們索引一個尚未出現的檢索詞時,我們必須創建一個新的集合。否則我們可以添加一個新的元素到一個現有的集合。在這種情況下,`set.add`修改位于`index`里面的集合,但不會修改`index`本身。我們唯一修改`index`的時候是添加一個新的檢索詞。 最后,`get`方法接受檢索詞并返回相應的`TermCounter`對象集。 這種數據結構比較復雜。回顧一下,`Index`包含`Map`,將每個檢索詞映射到`TermCounter`對象的`Set`,每個`TermCounter`包含一個`Map`,將檢索詞映射到計數。 ![](https://img.kancloud.cn/5a/12/5a12983eb60cba7001020fbce304ab04_616x379.jpg) 圖 8.1 `Index`的對象圖 圖 8.1 是展示這些對象的對象圖。`Index`對象具有一個名為`index` 的`Map`實例變量。在這個例子中,`Map`只包含一個字符串,`"Java"`,它映射到一個`Set`,包含兩個`TermCounter`對象的,代表每個出現單詞“Java”的頁面。 每個`TermCounter`包含`label`,它是頁面的 URL,以及`map`,它是`Map`,包含頁面上的單詞和每個單詞出現的次數。 `printIndex`方法展示了如何解壓縮此數據結構: ```java public void printIndex() { // loop through the search terms for (String term: keySet()) { System.out.println(term); // for each term, print pages where it appears and frequencies Set<TermCounter> tcs = get(term); for (TermCounter tc: tcs) { Integer count = tc.get(term); System.out.println(" " + tc.getLabel() + " " + count); } } } ``` 外層循環遍歷檢索詞。內層循環迭代`TermCounter`對象。 運行`ant build`來確保你的源代碼已編譯,然后運行`ant Index`。它下載兩個維基百科頁面,對它們進行索引,并打印結果;但是當你運行它時,你將看不到任何輸出,因為我們已經將其中一個方法留空。 你的工作是填寫`indexPage`,它需要一個 URL(一個`String`)和一個`Elements`對象,并更新索引。下面的注釋描述了應該做什么: ```java public void indexPage(String url, Elements paragraphs) { // 生成一個 TermCounter 并統計段落中的檢索詞 // 對于 TermCounter 中的每個檢索詞,將 TermCounter 添加到索引 } ``` 它能工作之后,再次運行`ant Index`,你應該看到如下輸出: ```java ... configurations http://en.wikipedia.org/wiki/Programming_language 1 http://en.wikipedia.org/wiki/Java_(programming_language) 1 claimed http://en.wikipedia.org/wiki/Java_(programming_language) 1 servletresponse http://en.wikipedia.org/wiki/Java_(programming_language) 2 occur http://en.wikipedia.org/wiki/Java_(programming_language) 2 ``` 當你運行的時候,檢索詞的順序可能有所不同。 同樣,運行`ant TestIndex`來確定完成了這部分練習。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看