Pregunta

Acabo de aprender sobre la clase de escáner de Java y ahora me pregunto cómo se compara/compite con StringTokenizer y String.split. Sé que el stringTokenizer y String.split solo funcionan en cadenas, entonces, ¿por qué querría usar el escáner para una cadena? ¿El escáner está destinado a ser una ventanilla única para dividir?

¿Fue útil?

Solución

Son esencialmente caballos para cursos.

  • Scanner está diseñado para casos en los que necesita analizar una cadena, extrayendo datos de diferentes tipos. Es muy flexible, pero podría decirse que no le da la API más simple para simplemente obtener una variedad de cuerdas delimitadas por una expresión particular.
  • String.split() y Pattern.split() Déle una sintaxis fácil para hacer lo último, pero eso es esencialmente todo lo que hacen. Si desea analizar las cadenas resultantes, o cambiar el delimitador a mitad de camino dependiendo de un token particular, no lo ayudarán con eso.
  • StringTokenizer es aún más restrictivo que String.split(), y también un poco más violento para usar. Está esencialmente diseñado para extraer tokens delimitados por sustras fijas. Debido a esta restricción, es aproximadamente el doble de rápido que String.split(). (Mira mi comparación de String.split() y StringTokenizer.) También es anterior a la API de expresiones regulares, de la cual String.split() es una parte.

Notarás de mis tiempos que String.split() todavía puede tokenizar Miles de cuerdas en unos pocos milisegundos en una máquina típica. Además, tiene la ventaja sobre StringTokenizer que le da la salida como una matriz de cadenas, que generalmente es lo que desea. Usando un Enumeration, según lo dispuesto por StringTokenizer, es demasiado "sintácticamente quisquilloso" la mayor parte del tiempo. Desde este punto de vista, StringTokenizer es un poco de espacio de espacio hoy en día, y también puede usar solo String.split().

Otros consejos

Comencemos eliminando StringTokenizer. Está envejeciendo y ni siquiera admite expresiones regulares. Su documentación establece:

StringTokenizer es una clase heredada que se conserva por razones de compatibilidad, aunque su uso se desaconseja en el nuevo código. Se recomienda que cualquier persona que busque esta funcionalidad use la split método de String o el java.util.regex paquete en su lugar.

Así que vamos a echarlo de inmediato. Eso deja split() y Scanner. ¿Cual es la diferencia entre ellos?

Por una cosa, split() Simplemente devuelve una matriz, lo que facilita el uso de un bucle foreach:

for (String token : input.split("\\s+") { ... }

Scanner se construye más como una transmisión:

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

o

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(Tiene un poco API grande, así que no pienses que siempre está restringido a cosas tan simples).

Esta interfaz de estilo de transmisión puede ser útil para analizar archivos de texto simples o entrada de consola, cuando no tiene (o no puede obtener) toda la entrada antes de comenzar a analizar.

Personalmente, la única vez que recuerdo haber usado Scanner es para proyectos escolares, cuando tuve que obtener la entrada del usuario de la línea de comando. Hace que ese tipo de operación sea fácil. Pero si tengo un String que quiero dividir, es casi obvio acompañar split().

StringTokenizer siempre estaba ahí. Es el más rápido de todo, pero el idioma de enumeración podría no parecer tan elegante como los demás.

Split llegó a existir en JDK 1.4. Más lento que el tokenizer pero más fácil de usar, ya que se puede llamar desde la clase de cadena.

El escáner llegó a estar en JDK 1.5. Es el más flexible y llena un vacío de larga data en la API de Java para apoyar un equivalente de la famosa familia de la función CS ScanF.

Split es lento, pero no tan lento como el escáner. StringTokenizer es más rápido que la división. Sin embargo, descubrí que podía obtener el doble de la velocidad, intercambiando cierta flexibilidad, para obtener un impulso de velocidad, lo que hice en JFastParser https://github.com/hughperkins/jfastparser

Prueba en una cadena que contiene un millón de dobles:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

Si tiene un objeto de cadena que desea tokenizar, favorezca el uso de cadenas separar Método sobre un StringTokenizer. Si analiza los datos de texto de una fuente fuera de su programa, como desde un archivo o del usuario, ahí es donde un escáner es útil.

String.split parece ser mucho más lento que StringTokenizer. La única ventaja con Split es que obtienes una matriz de los tokens. También puede usar cualquier expresión regular en Split. org.apache.commons.lang.stringutils tiene un método dividido que funciona mucho más rápido que cualquiera de los dos a saber. StringTokenizer o String.split. Pero la utilización de la CPU para los tres es casi la misma. Por lo tanto, también necesitamos un método que sea menos intensivo en CPU, que todavía no puedo encontrar.

Recientemente hice algunos experimentos sobre el mal rendimiento de String.split () en situaciones altamente sensibles al rendimiento. Puede encontrar esto útil.

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

El GIST es que String.split () compila un patrón de expresión regular cada vez y, por lo tanto, puede ralentizar su programa, en comparación con si usa un objeto de patrón precompilado y lo usa directamente para operar en una cadena.

Para los escenarios predeterminados, sugeriría patrón.

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

Use "ABC" .toCarArray () para obtener la matriz de char para una cadena. Por ejemplo:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');

Una diferencia importante es que tanto String.split () como el escáner pueden producir cadenas vacías, pero StringTokenizer nunca lo hace.

Por ejemplo:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

Producción:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

Esto se debe a que el delimitador para String.split () y Scanner.usedElimiter () no es solo una cadena, sino una expresión regular. Podemos reemplazar el delimitador "" con " +" en el ejemplo anterior para que se comporten como StringTokenizer.

String.split () funciona muy bien pero tiene sus propios límites, como si quisiera dividir una cadena como se muestra a continuación en función del símbolo de tubería simple o doble (|), no funciona. En esta situación puede usar StringTokenizer.

ABC | IJK

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top