2011-06-07 15 views
8

He intentado actualizar mis habilidades de Java para utilizar Java 5 & Java 6. He estado jugando con algunos ejercicios de programación. Me pidieron que leyera en un párrafo de un archivo de texto y sacara una lista ordenada (descendente) de palabras y sacara el conteo de cada palabra.¿Más eficiente o más moderno? Lectura y clasificación de un archivo de texto con Java

Mi código está por debajo.

Mis preguntas son:

  1. Es mi rutina de entrada de archivo el más respetuoso de los recursos de JVM?

  2. ¿Es posible reducir los pasos en lo que respecta a leer el contenido del archivo y obtener el contenido en una colección que puede hacer una lista ordenada de palabras?

  3. ¿Estoy usando las clases de Colección e interfaz de la manera más eficiente que puedo?

Muchas gracias por cualquier opinión. Solo intento divertirme un poco y mejorar mis habilidades de programación.

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

Lo primero que se destaca es su fondo C++. Puede obtener más de los ejercicios si intenta hacer que sus soluciones estén orientadas a objetos, incluso si las preguntas no lo solicitan específicamente. Hacerlo más orientado a objetos lo hará pensar acerca de cómo agrupar la funcionalidad en clases lógicas y ocultar detalles de implementación detrás de llamadas a métodos más convenientes. Dicho esto, es hora de leer más sobre su código y abordar su pregunta más directamente ... –

+2

sus convenciones de nombres son atroces para Java moderno. La notación húngara que no es uniforme no es idiomática para Java de ninguna versión. El uso directo de 'Array' también está mal visto, hay clases' List' y 'Set' que son más idiomáticas también. –

+0

Jarrod. Entiendo el comentario sobre la notación húngara. ¿Por qué las clases List o Set son mejores que usar una matriz en esta situación? – Steve

Respuesta

4
  1. hay formas más idiomáticas de la lectura en todas las palabras en un archivo en Java. BreakIterator es una forma mejor de leer en palabras desde una entrada.

  2. Use List<String> en lugar de Array en casi todos los casos. Array no es técnicamente parte del Collection API y no es tan fácil de reemplazar implementaciones como List, Set y Map.

  3. Debe usar un Map<String,AtomicInteger> para contar su palabra en lugar de caminar el Array una y otra vez. AtomicInteger es mutable a diferencia de Integer, por lo que puede simplemente incrementAndGet() en una sola operación que resulte segura para hilos. Una implementación de SortedMap le daría las palabras en orden con sus recuentos también.

  4. Make as many variables, even local ones final as possible. y declararlos justo antes de usarlos, no en la parte superior donde se perderá su alcance previsto.

  5. Casi siempre debe usar un BufferedReader o BufferedStream con un tamaño de búfer apropiado igual a un múltiplo del tamaño de su bloque de disco al hacer el disco IO.

Dicho esto, no se preocupe por las micro optimizaciones hasta que tenga un comportamiento "correcto".

2
  • el tipo SortedMap podría ser eficiente memoria suficiente en cuanto a usar aquí en forma SortedMap<String,Integer> (especialmente si la palabra que cuenta son propensos a estar bajo 128)
  • puede proporcionar delimitadores de los clientes con el tipo Scanner para romper arroyos

Dependiendo de cómo desea tratar los datos, también puede ser que desee para despojar puntuacion o ir para el aislamiento palabra más avanzada con un iterador de ruptura - ver el paquete de java.text o el proyecto de la UCI.

También recomiendo declarar variables cuando las asigna por primera vez y deja de asignar valores nulos no deseados.


Elaborar, puede contar las palabras en un mapa como este:

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

Debido a la inmutabilidad de Integer y el comportamiento de autoboxing, esto podría result in excessive object instantiation para grandes conjuntos de datos. Una alternativa sería (como otros sugieren) utilizar un envoltorio int mutable (de las cuales es una forma AtomicInteger.)

+0

+1 para OrderedMap. Estaba pensando en un viejo HashMap, pero OrderedMap lo haría aún más fácil. –

+0

Hola McDowell; Usar Scanner suena como una buena idea. Los mapas son para almacenar pares de valores clave y solo quiero obtener una lista de elementos únicos, sin pares. ¿Sugiere que use un Mapa para su API y simplemente haga que la clave y el valor sean la misma Cadena? – Steve

+0

@ user787832 - puede usar el mapa para almacenar las palabras (claves) y los conteos de palabras (valores). – McDowell

0

Se puede utilizar Guava por su tarea? Multiset maneja el conteo. Específicamente, LinkedHashMultiset podría ser útil.

+0

Hola djg; Lo creas o no, esto no es tarea. Es solo yo tratando de arreglarme buscando en Google "código kata". No estaba al tanto de la guayaba. Gracias. Estoy tratando de mantener el estándar Java por el momento. – Steve

0

Algunas otras cosas que puedes encontrar interesantes:

para leer el archivo se puede utilizar un BufferedReader (si se trata de sólo texto).

Este:

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

se podría hacer utilizando una extendida para-bucle (el Java-foreach), como se muestra here.

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

En su caso, puede simplemente usar una sola else por lo que la condición no se comprueba de nuevo (porque si las palabras no son los mismos, sólo pueden ser diferentes).

if (!pastword.equals("")) 

Creo que es más rápido usando length aquí:

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

Para este último punto, si va a usar .equals(), primero debe usar la constante, es decir, 'if (" ".equals (palabra pasada))' - para evitar posibles 'NullPointerException's. –

0

Método de entrada:

que sea más fácil en sí mismo y tratan directamente con los personajes en lugar de bytes. Por ejemplo, podría usar un FileReader y posiblemente envolverlo dentro de un BufferedReader. Por lo menos, sugiero mirar InputStreamReader, ya que la implementación para cambiar de bytes a caracteres ya está hecha para usted. Mi preferencia sería usar Scanner.

Preferiría devolver null o lanzar una excepción de su método readIn().Las excepciones no se deben usar para el control de flujo, pero, aquí, está enviando un mensaje importante a la persona que llama: el archivo que proporcionó no era válido. Lo que me lleva a otro punto: considere si realmente desea detectar todas las excepciones, o solo las de ciertos tipos. Tendrá que manejar todas las excepciones marcadas, pero es posible que desee manejarlas de manera diferente.

Colecciones:

Estás realmente no utiliza clases Colecciones, que están utilizando una matriz. Su implementación parece estar bien, pero ...

Sin duda hay muchas maneras de manejar este problema. Su método - ordenando luego comparando con el último - es O (nlogn) en promedio. Eso ciertamente no está mal. Mire una forma de usar una implementación Map (como HashMap) para almacenar los datos que necesita mientras solo cruza el texto en O (n) (HashMap) get() y put() - y presumiblemente - los métodos son O (1))

+0

Hmm ... cuando escribí que no noté que su salida debía estar en orden ordenado. Desafortunadamente, no lo obtendrás por debajo de O (nlogn), pero sigo creyendo que usar una implementación de 'Mapa' será mejor. –

Cuestiones relacionadas