Более эффективный или более современный? Чтение и сортировка текстового файла с помощью Java - PullRequest
8 голосов
/ 07 июня 2011

Я пытался улучшить свои навыки Java, чтобы использовать больше Java 5 и Java 6. Я играл с некоторыми упражнениями по программированию. Меня попросили прочитать абзац из текстового файла, вывести отсортированный (нисходящий) список слов и вывести количество каждого слова.

Мой код указан ниже.

Мои вопросы:

  1. Является ли моя процедура ввода файлов наиболее уважительным из ресурсов JVM?

  2. Можно ли сократить шаги, связанные с чтением содержимого файла и передачей содержимого в коллекцию, которая может составить отсортированный список слов?

  3. Использую ли я классы Collection и интерфейс наиболее эффективным способом?

Большое спасибо за любые мнения. Я просто пытаюсь развлечься и улучшить свои навыки программирования.

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 ]

4 голосов
/ 07 июня 2011
  1. В Java есть более идиоматические способы чтения всех слов в файле. BreakIterator - лучший способ чтения слов из ввода.

  2. Использование List<String> вместо Array почти во всех случаях,Массив технически не является частью Collection API и не так прост для замены реализаций, как List, Set и Map.

  3. Вы должны использоватьMap<String,AtomicInteger> для подсчета слов вместо того, чтобы ходить по Array снова и снова. AtomicInteger является изменяемым в отличие от Integer, поэтому вы можете просто incrementAndGet() в одной операции, которая оказывается поточно-ориентированной.Реализация SortedMap выдала бы вам слова в порядке с их количеством.

  4. Создайте как можно больше переменных, даже локальных final. и объявляйте их прямо перед тем, как использовать их, а не сверху, где их предполагаемая область видимости будет потеряна.

  5. Вы почти всегда должны использовать BufferedReader или BufferedStream с соответствующим буферомразмер, кратный размеру вашего дискового блока при выполнении дискового ввода-вывода.

Тем не менее, не занимайтесь микрооптимизацией, пока у вас не будет "правильного" поведения.

2 голосов
/ 07 июня 2011
  • тип SortedMap может быть достаточно эффективным с точки зрения памяти, чтобы использовать его здесь в виде SortedMap<String,Integer> (особенно если число слов может быть меньше 128)
  • Вы можете предоставить разделители клиентов для типа Сканер для прерывания потоков

В зависимости от того, как вы хотите обрабатывать данные, вы также можете убрать пунктуацию или перейти кболее продвинутая изоляция слов с помощью итератора разрыва - см. пакет java.text или проект ICU.

Также - я рекомендую объявлять переменные при первом их назначении и прекращении назначения нежелательных нулевых значений.


Для уточнения можно посчитать слова на карте следующим образом:

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

Из-за неизменности Integer и поведения автобоксаэто может привести к чрезмерной реализации объекта для больших наборов данных.Альтернативой может быть (как предлагают другие) использование изменяемой оболочки int (из которых AtomicInteger является формой.)

0 голосов
/ 07 июня 2011

Метод ввода:

Упростите для себя и работайте напрямую с символами, а не с байтами.Например, вы можете использовать FileReader и, возможно, обернуть его внутри BufferedReader.По крайней мере, я бы посоветовал взглянуть на InputStreamReader, так как реализация перехода от байтов к символам уже сделана для вас.Я предпочел бы использовать Scanner.

. Я бы предпочел вернуть null или вызвать исключение из вашего readIn() метода.Исключения не должны использоваться для управления потоком, но здесь вы отправляете вызывающему важное сообщение: предоставленный вами файл недействителен.Это подводит меня к другому вопросу: подумайте, действительно ли вы хотите перехватить все исключения или только некоторые из них.Вам придется обрабатывать все проверенные исключения, но, возможно, вы захотите обрабатывать их по-другому.

Коллекции:

Вы действительно не используете классы коллекций, вы используете массив.Ваша реализация выглядит нормально, но ...

Конечно, есть много способов решения этой проблемы.Ваш метод - сортировка, затем сравнение с последним - в среднем O (nlogn).Это конечно не плохо.Посмотрите, как можно использовать реализацию Map (например, HashMap) для хранения необходимых вам данных при обходе только текста в O (n) (HashMap * get() и put() - ипредположительно contains() - методы O (1)).

0 голосов
/ 07 июня 2011

Некоторые другие вещи, которые могут вас заинтересовать:

Для чтения файла вы можете использовать BufferedReader (если это только текст).

Это:

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

Может быть сделано с использованием расширенного цикла for (Java-foreach), как показано здесь .

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

В вашем случае вы можете просто использовать один else, чтобы условие не проверялось снова (потому что, если слова не совпадают, они могут отличаться только).

if ( !pastword.equals("") )

Я думаю, что использование length здесь быстрее:

if (!pastword.length == 0)
0 голосов
/ 07 июня 2011

Можете ли вы использовать Гуава для домашнего задания?Multiset обрабатывает счет.В частности, LinkedHashMultiset может быть полезным.

...