更高效还是更现代?阅读&用Java排序文本文件

时间:2011-06-07 16:40:59

标签: java file sorting text collections

我一直在尝试升级我的Java技能,以便使用更多的Java 5& Java 6.我一直在玩一些编程练习。我被要求从文本文件中读取一个段落并输出一个排序的(降序)单词列表并输出每个单词的计数。

我的代码如下。

我的问题是:

  1. 我的文件输入例程是否是最尊重JVM资源的?

  2. 是否可以在阅读文件内容和将内容放入可以制作排序单词列表的集合方面减少步骤?

  3. 我是否以最有效的方式使用Collection类和界面?

  4. 非常感谢任何意见。我只是想尝试一些乐趣并提高我的编程技巧。

    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
    

5 个答案:

答案 0 :(得分:4)

  1. Java中的文件中所有单词都有更多惯用的阅读方式。  BreakIterator是从输入中读取单词的更好方式。

  2. 几乎在所有情况下都使用List<String>代替Array。数组在技术上不属于Collection API,并且不像ListSetMap那样容易替换实现。

  3. 您应该使用Map<String,AtomicInteger>进行单词计数,而不是一遍又一遍地走Array。与Integer不同,AtomicInteger是可变的,因此您可以在单个操作中incrementAndGet()恰好是线程安全的。 SortedMap实施将按顺序为您提供单词。

  4. Make as many variables, even local ones final as possible.并在您使用它们之前声明它们,而不是在其预期范围将丢失的顶部。

  5. 在执行磁盘IO时,您应该始终使用BufferedReaderBufferedStream,其缓冲区大小等于磁盘块大小的倍数。

  6. 那就是说,在你有“正确”行为之前,不要关心微观优化。

答案 1 :(得分:2)

  • SortedMap类型可能在内容方面非常有效,可以在SortedMap<String,Integer>格式中使用(特别是如果字数可能低于128)
  • 您可以为Scanner类型提供客户分隔符以打破流

根据您希望如何处理数据,您可能还想删除标点符号或使用break iterator进行更高级的单词隔离 - 请参阅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是一种形式。)

答案 2 :(得分:0)

您可以使用Guava作业吗? Multiset处理计数。具体来说,LinkedHashMultiset可能很有用。

答案 3 :(得分: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)

答案 4 :(得分:0)

输入法:

让自己更轻松,直接处理字符而不是字节。例如,您可以使用FileReader并将其包含在BufferedReader中。至少,我建议查看InputStreamReader,因为已经为您完成了从字节到字符的更改。我的偏好是使用Scanner

我希望返回null或从您的readIn()方法中抛出异常。不应将异常用于流控制,但是,在这里,您将向调用方发送一条重要消息:您提供的文件无效。这让我想到另一点:考虑你是否真的想要捕获所有异常,或者只是某些类型的异常。您必须处理所有已检查的异常,但您可能希望以不同方式处理它们。

类别:

你真的没有使用Collections类,你正在使用数组。你的实现似乎很好,但是......

当然有很多方法可以解决这个问题。您的方法 - 排序然后与最后一次比较 - 平均为O(nlogn)。那肯定不错。查看使用Map实现(例如HashMap)来存储所需数据的方法,同时仅遍历O(n)中的文本(HashMap'{{1} }和get() - 大概是put() - 方法是O(1))。