2011-06-07 23 views
8

我一直在嘗試升級Java技能以使用更多的Java 5 & Java 6。我一直在玩一些編程練習。我被要求從一個文本文件中讀取一段文字,並輸出一個排序(降序)的單詞列表並輸出每個單詞的計數。更高效還是更現代?用Java讀入和排序文本文件

我的代碼如下。

我的問題是:

  1. 是我的文件輸入程序最恭敬JVM的資源?

  2. 是否可以減少讀取文件內容和將內容導入可以創建排序列表的單詞的步驟?

  3. 我使用集合類和接口是最有效的方式嗎?

非常感謝您的意見。我只是想獲得一些樂趣並提高我的編程技能。

import java.io.*; 
import java.util.*; 

public class Sort 
{ 
    public static void main(String[] args) 
    { 
     String sUnsorted  = null; 
     String[] saSplit   = null; 

     int iCurrentWordCount = 1; 
     String currentword  = null; 
     String pastword   = ""; 

     // Read the text file into a string 
     sUnsorted = readIn("input1.txt"); 

     // Parse the String by white space into String array of single words 
     saSplit = sUnsorted.split("\\s+"); 

     // Sort the String array in descending order 
     java.util.Arrays.sort(saSplit, Collections.reverseOrder()); 


     // Count the occurences of each word in the String array 
     for (int i = 0; i < saSplit.length; i++) 
     { 

      currentword = saSplit[i]; 

      // If this word was seen before, increase the count & print the 
      // word to stdout 
      if (currentword.equals(pastword)) 
      { 
       iCurrentWordCount ++; 
       System.out.println(currentword); 
      } 
      // Output the count of the LAST word to stdout, 
      // Reset our counter 
      else if (!currentword.equals(pastword)) 
      { 

       if (!pastword.equals("")) 
       { 

        System.out.println("Word Count for " + pastword + ": " + iCurrentWordCount); 

       } 


       System.out.println(currentword); 
       iCurrentWordCount = 1; 

      } 

      pastword = currentword; 
     }// end for loop 

     // Print out the count for the last word processed 
     System.out.println("Word Count for " + currentword + ": " + iCurrentWordCount); 



    }// end funciton main() 


    // Read The Input File Into A String  
    public static String readIn(String infile) 
    { 
     String result = " "; 

     try 
     { 
      FileInputStream file = new FileInputStream (infile); 
      DataInputStream in = new DataInputStream (file); 
      byte[] b    = new byte[ in.available() ]; 

      in.readFully (b); 
      in.close(); 

      result = new String (b, 0, b.length, "US-ASCII"); 

     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 

     return result; 
    }// end funciton readIn() 

}// end class Sort() 

///////////////////////////////////////////////// 
// Updated Copy 1, Based On The Useful Comments 
////////////////////////////////////////////////// 

import java.io.*; 
import java.util.*; 

public class Sort2 
{ 
    public static void main(String[] args) throws Exception 
    { 
     // Scanner will tokenize on white space, like we need 
     Scanner scanner    = new Scanner(new FileInputStream("input1.txt")); 
     ArrayList <String> wordlist = new ArrayList<String>(); 
     String currentword   = null; 
     String pastword    = null; 
     int iCurrentWordCount   = 1;  

     while (scanner.hasNext()) 
      wordlist.add(scanner.next()); 

     // Sort in descending natural order 
     Collections.sort(wordlist); 
     Collections.reverse(wordlist); 

     for (String temp : wordlist) 
     { 
      currentword = temp; 

      // If this word was seen before, increase the count & print the 
      // word to stdout 
      if (currentword.equals(pastword)) 
      { 
       iCurrentWordCount ++; 
       System.out.println(currentword); 
      } 
      // Output the count of the LAST word to stdout, 
      // Reset our counter 
      else //if (!currentword.equals(pastword)) 
      { 
       if (pastword != null) 
        System.out.println("Count for " + pastword + ": " + 
                  CurrentWordCount); 

       System.out.println(currentword); 
       iCurrentWordCount = 1;  
      } 

      pastword = currentword; 
     }// end for loop 

     System.out.println("Count for " + currentword + ": " + iCurrentWordCount); 

    }// end funciton main() 


}// end class Sort2 
+0

突出的第一件事就是您的C++背景。如果您嘗試使解決方案面向對象,即使問題沒有具體要求,您也可以從練習中獲得更多。讓它更加面向對象將讓你思考如何將功能組合到邏輯類中,並隱藏更方便的方法調用後面的實現細節。也就是說,有時間閱讀更多的代碼並更直接地解決您的問題...... – 2011-06-07 16:46:36

+2

您的命名約定對於現代Java而言是非常殘酷的。對任何版本的Java來說,甚至不一致的匈牙利符號都不是慣用的!直接使用'Array'也被忽略了,還有'List'和'Set'類更具慣用性。 – 2011-06-07 16:47:38

+0

Jarrod。我瞭解有關匈牙利符號的評論。爲什麼List或Set類比在這種情況下使用Array更好? – Steve 2011-06-07 17:10:43

回答

4
  1. 有很多在Java中的文件中的所有單詞閱讀更地道的方式。 BreakIterator是從輸入中讀取單詞的更好方法。

  2. 在幾乎所有情況下都使用List<String>而不是Array。陣列在技術上不是​​的一部分,並且不像List,SetMap那樣容易替換實現。

  3. 你應該使用Map<String,AtomicInteger>做你的字數統計,而不是一遍又一遍地走ArrayAtomicIntegerInteger不同,因此您只需incrementAndGet()即可。一個SortedMap實現會給你的話,以及他們的計數。

  4. Make as many variables, even local ones final as possible.並且在你使用它們之前聲明它們,而不是在它們的預期範圍將會丟失的頂部。

  5. 在執行磁盤IO時,您應該幾乎總是使用BufferedReaderBufferedStream,其緩衝區大小等於磁盤塊大小的倍數。

也就是說,在你有「正確的」行爲之前,不要關注微觀優化。

2
  • SortedMap類型可能是高效的內存不夠明智的形式SortedMap<String,Integer>(尤其是如果字計數可能是128以下)
  • 可以提供客戶分隔符爲Scanner型打破在這裏使用流

根據您想如何處理數據,您可能還想要去除標點符號或去更高級的字隔離帶突破​​迭代器 - 看到java.text包或ICU項目。

另外 - 我建議首次分配變量時聲明變量並停止分配不需要的空值。


要詳細,你可以在地圖這樣算的話:

void increment(Map<String, Integer> wordCountMap, String word) { 
    Integer count = wordCountMap.get(word); 
    wordCountMap.put(word, count == null ? 1 : ++count); 
} 

由於對Integer不變性和自動裝箱的大數據集的行爲,這可能result in excessive object instantiation。另一種方法是(正如其他建議)使用可變int包裝(其中AtomicInteger是一種形式。)

+0

對於OrderedMap爲+1。我在想一個普通的舊HashMap,但OrderedMap會讓事情變得更容易。 – 2011-06-07 17:17:07

+0

嗨麥克道爾;使用掃描儀聽起來像一個整潔的想法。地圖用於存儲鍵值對,我只想獲得單個非配對項目的列表。你是否建議我爲它的API使用一個Map,並且只是讓鍵和值相同的字符串? – Steve 2011-06-07 17:18:00

+0

@ user787832 - 您可以使用地圖來存儲單詞(鍵)和單詞計數(值)。 – McDowell 2011-06-07 17:37:54

0

您可以用Guava你的家庭作業? Multiset處理計數。具體而言,LinkedHashMultiset可能會有用。

+0

嗨djg;相信與否,這不是功課。這只是我試圖通過搜索「code kata」來改變自己。我不知道番石榴。謝謝。我試圖堅持使用標準的Java。 – Steve 2011-06-07 17:14:24

0

一些其他的事情,你可能會感興趣:

讀取該文件,你可以使用一個BufferedReader(如果是純文本)。

此:

for (int i = 0; i < saSplit.length; i++){ 
    currentword = saSplit[i]; 
    [...] 
} 

能使用的擴展for循環(在Java-的foreach)來完成,如所示here

if (currentword.equals(pastword)){ 
    [...] 
} else if (!currentword.equals(pastword)) { 
    [...] 
} 

在你的情況,你可以簡單地使用一個else這樣的情況不會再次檢查(因爲如果的話是不一樣的,它們只能是不同的)。

if (!pastword.equals("")) 

我認爲使用length更快這裏:

if (!pastword.length == 0) 
+0

對於最後一點,如果您打算使用.equals(),您應該首先使用常量 - 即「if(」「.equals(pastword))' - 以避免可能的'NullPointerException's。 – 2011-06-07 17:19:52

0

輸入法:

使用戶更容易對自己,並直接與字符,而不是字節處理。例如,您可以使用FileReader,並可能將其包裝在BufferedReader中。至少,我建議看看InputStreamReader,因爲已經爲您完成了從字節到字符的更改。我的偏好是使用Scanner

我寧願返回null或從您的readIn()方法拋出異常。例外情況不應該用於流量控制,但是,在這裏,您正在向調用方發送重要消息:您提供的文件無效。這給我帶來了另一點:考慮你是否真的想要捕獲所有的異常,或只是某些類型的異常。你必須處理所有檢查的異常,但你可能想要以不同的方式處理它們。

類別:

你真的不使用Collections類,您使用的是數組。你的實現似乎很好,但...

當然有很多方法來處理這個問題。你的方法 - 排序然後與上一次比較 - 平均爲O(nlogn)。這當然不壞。看看如何使用Map實現(例如HashMap)來存儲所需的數據,而僅遍歷O(n)中的文本(HashMapget()put() - 推測爲 - 方法爲O(1) )。

+0

嗯...當我寫道,我沒有注意到你的輸出是按照排序順序。不幸的是,你不會在O(nlogn)之下得到那個,但我仍然認爲使用'Map'實現會更好。 – 2011-06-07 17:17:01

相關問題