簡體   English   中英

計算文本中單詞出現的次數 java

[英]counting number of occurrences of words in a text java

因此,我正在從頭開始構建 TreeMap,並嘗試使用 Java 計算文本中每個單詞的出現次數。 文本是從文本文件中讀取的,但我可以輕松地從那里讀取。 我真的不知道如何計算每個單詞,有人可以幫忙嗎?

想象一下文本是這樣的:

隨着時間的推移,計算機工程師會利用彼此的工作並為新事物發明算法。 算法與其他算法相結合以利用其他算法的結果,進而產生更多算法的結果。

Output: 
Over 1
time 1
computer 1
algotitms 5
...

如果可能的話,我想忽略它是大寫還是小寫,我想將它們一起計算。

編輯:我不想使用任何類型的 Map (hashMap ie) 或類似的東西來做到這一點。

將問題分解如下(這是一種潛在的解決方案 - 不是解決方案):

  1. 將文本拆分為單詞(創建列表或數組或單詞)。
  2. 去掉標點符號。
  3. 創建您的 map 以收集結果。
  4. 遍歷您的單詞列表並將“1”添加到每個遇到的鍵的值
  5. 顯示結果(遍歷地圖的EntrySet

將文本拆分為單詞

我的偏好是使用空格作為分隔符來分割單詞。 原因是,如果您使用非單詞字符進行拆分,您可能會錯過一些連字符。 我知道連字符的使用正在減少,仍然有很多單詞屬於這條規則; 例如,中年人。 如果遇到這樣的單詞,它可能必須被視為一個單詞而不是兩個單詞。

去除標點符號

由於上述決定,您需要首先刪除可能附加在您的單詞上的標點符號。 請記住,如果您使用正則表達式來拆分單詞,您可能可以在執行上述步驟的同時完成此步驟。 事實上,這將是首選,這樣您就不必迭代兩次。 一次性完成這兩項工作。 當您使用它時,在輸入字符串上調用toLowerCase()以消除大寫單詞和小寫單詞之間的歧義。

創建您的 map 以收集結果

這是您要收集計數的地方。 使用 Java MapTreeMap實現。 關於這個特定實現需要注意的一件事是map 根據其鍵的自然順序進行排序 在這種情況下,由於鍵是輸入文本中的單詞,因此鍵將按字母順序排列,而不是按計數的大小排列。 如果按計數對條目進行排序很重要,則有一種技術可以“反轉” map 並使值成為鍵,鍵成為值。 但是,由於兩個或多個單詞可能具有相同的計數,因此您需要創建一個 <Integer, Set> 的新 map,以便您可以將具有相同計數的單詞組合在一起。

遍歷您的單詞列表

此時,您應該有一個單詞列表和一個 map 結構來收集計數。 使用 lambda 表達式,您應該能夠非常輕松地執行count()或您的單詞。 但是,如果您不熟悉或不熟悉 Lambda 表達式,您可以使用常規循環結構來遍歷您的列表,執行containsKey() get()是否之前遇到過該單詞,如果 map 已經包含單詞,然后將“1”添加到前一個值。 最后, put()新計數放入 map 中。

顯示結果

同樣,您可以使用 Lambda 表達式打印出EntrySet鍵值對或簡單地遍歷條目集以顯示結果。

基於以上所有幾點,一個潛在的解決方案應該是這樣的(為了 OP 而不是使用 Lambda)

public static void main(String[] args) {
    String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
    
    text = text.replaceAll("\\p{P}", ""); // replace all punctuations
    text = text.toLowerCase(); // turn all words into lowercase
    String[] wordArr = text.split(" "); // create list of words

    Map<String, Integer> wordCount = new TreeMap<>();
    
    // Collect the word count
    for (String word : wordArr) {
        if(!wordCount.containsKey(word)){
            wordCount.put(word, 1);
        } else {
            int count = wordCount.get(word);
            wordCount.put(word, count + 1);
        }
    }
    
    Iterator<Entry<String, Integer>> iter = wordCount.entrySet().iterator();
    
    System.out.println("Output: ");
    while(iter.hasNext()) {
        Entry<String, Integer> entry = iter.next();
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }
}

這將產生以下 output

Output: 
advantage: 1
algorithms: 5
and: 1
combine: 1
computer: 1
each: 1
engineers: 1
even: 1
for: 2
in: 1
invent: 1
more: 1
new: 1
of: 2
other: 2
others: 1
over: 1
producing: 1
results: 2
take: 1
the: 1
things: 1
time: 1
to: 1
turn: 1
utilize: 1
with: 1
work: 1

為什么我要為如此平凡的任務分解這樣的問題? 簡單的。 我相信這些離散步驟中的每一個都應該被提取到函數中以提高代碼的可重用性。 是的,使用 Lambda 表達式一次完成所有操作並讓您的代碼看起來更加簡化,這很酷。 但是,如果您需要一遍又一遍地執行一些中間步驟怎么辦? 大多數時候,重復代碼來實現這一點。 實際上,通常更好的解決方案是將這些任務分解為方法。 其中一些任務,例如轉換輸入文本,可以在單一方法中完成,因為該活動似乎在本質上是相關的。 (有一種方法“做得太少”。)

public String[] createWordList(String text) {
    return text.replaceAll("\\p{P}", "").toLowerCase().split(" ");
}

public Map<String, Integer> createWordCountMap(String[] wordArr) {
    Map<String, Integer> wordCountMap = new TreeMap<>();

    for (String word : wordArr) {
        if(!wordCountMap.containsKey(word)){
            wordCountMap.put(word, 1);
        } else {
            int count = wordCountMap.get(word);
            wordCountMap.put(word, count + 1);
        }
    }

return wordCountMap;
}

String void displayCount(Map<String, Integer> wordCountMap) {
    Iterator<Entry<String, Integer>> iter = wordCountMap.entrySet().iterator();
    
    while(iter.hasNext()) {
        Entry<String, Integer> entry = iter.next();
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }
}

現在,在這樣做之后,您的main方法看起來更具可讀性,並且您的代碼更具可重用性。

public static void main(String[] args) {
    
    WordCount wc = new WordCount();
    String text = "...";
    
    String[] wordArr = wc.createWordList(text);
    Map<String, Integer> wordCountMap = wc.createWordCountMap(wordArr);
    wc.displayCount(wordCountMap);
}

更新

我忘記提及的一個小細節是,如果使用HashMap而不是TreeMap ,則 output 將按計數值降序排序。 這是因為散列 function 將使用條目的值作為 hash。 因此,您無需為此目的“反轉” map。 所以,切換到HashMap后, output 應該如下:

Output: 
algorithms: 5
other: 2
for: 2
turn: 1
computer: 1
producing: 1
...

我的建議是使用 regexp 和 split 以及 stream 進行分組我認為這就是你的意思,但我不確定我是否在列表中使用了太多

@Test
public void testApp() {
    final String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
    final String[] split = text.split("\\W+");
    final List<String> list = new ArrayList<>();
    System.out.println("Output: ");
    for (String s : split) {
        if(!list.contains(s)){
            list.add(s.toUpperCase());
            final long count = Arrays.stream(split).filter(s::equalsIgnoreCase).count();
            System.out.println(s+" "+count);
        }
    }

}

下面是您的示例的測試,但使用 MAP

@Test
public void test() {
    final String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
    Map<String, Long> result = Arrays.stream(text.split("\\W+")).collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
    assertEquals(result.get("algorithms"), new Long(5));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM