Domanda

Ho appena imparato sulla classe di scanner di Java e ora mi chiedo come si confronta/compete con StringTokenizer e String.split. So che StringTokenizer e String.Split funzionano solo sulle stringhe, quindi perché dovrei usare lo scanner per una stringa? Lo scanner è solo destinato ad essere un punto di spicco per la giunzione?

È stato utile?

Soluzione

Sono essenzialmente cavalli per i corsi.

  • Scanner è progettato per i casi in cui è necessario analizzare una stringa, estraendo dati di diversi tipi. È molto flessibile, ma probabilmente non ti dà l'API più semplice per ottenere semplicemente una serie di stringhe delimitate da una particolare espressione.
  • String.split() e Pattern.split() Dai una semplice sintassi per fare quest'ultimo, ma è essenzialmente tutto ciò che fanno. Se vuoi analizzare le stringhe risultanti o cambiare il delimitatore a metà strada a seconda di un particolare token, non ti aiuteranno.
  • StringTokenizer è ancora più restrittivo di String.split(), e anche un po 'più fiddlier da usare. È essenzialmente progettato per estrarre token delimitati da substrings fissi. A causa di questa restrizione, è circa il doppio di String.split(). (Guarda il mio confronto di String.split() e StringTokenizer.) Preda anche l'API delle espressioni regolari, di cui String.split() è una parte.

Noterai dai miei tempi String.split() può ancora tokenizzare migliaia di corde in pochi millisecondi Su una macchina tipica. Inoltre, ha il vantaggio StringTokenizer che ti dà l'output come array di stringhe, che di solito è quello che vuoi. Usando un Enumeration, come fornito da StringTokenizer, è troppo "sintatticamente pignolo" per la maggior parte del tempo. Da questo punto di vista, StringTokenizer è un po 'uno spreco di spazio al giorno d'oggi e puoi anche usare String.split().

Altri suggerimenti

Cominciamo eliminando StringTokenizer. Sta invecchiando e non supporta nemmeno espressioni regolari. La sua documentazione afferma:

StringTokenizer è una lezione legacy che viene mantenuta per motivi di compatibilità sebbene il suo uso sia scoraggiato nel nuovo codice. Si consiglia a chiunque cerchi questa funzionalità utilizzare il split metodo di String o il java.util.regex pacchetto invece.

Quindi gettiamolo subito. Che lascia split() e Scanner. Qual è la differenza tra loro?

Per una cosa, split() Restituisce semplicemente un array, che rende facile utilizzare un ciclo foreach:

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

Scanner è costruito più come un flusso:

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

o

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

(Ha piuttosto un grande api, quindi non pensare che sia sempre limitato a cose così semplici.)

Questa interfaccia in stile flusso può essere utile per analizzare semplici file di testo o input di console, quando non si dispone (o non si può ottenere) tutti gli input prima di iniziare a analizzare.

Personalmente, l'unica volta che ricordo di aver usato Scanner è per i progetti scolastici, quando ho dovuto ottenere input dell'utente dalla riga di comando. Rende facile questo tipo di operazione. Ma se ho un String Che voglio separarmi, è quasi un gioco da ragazzi con cui andare split().

StringTokenizer era sempre lì. È il più veloce di tutti, ma il linguaggio di enumerazione potrebbe non sembrare elegante come gli altri.

La divisione è arrivata all'esistenza su JDK 1.4. Più lento del tokenizer ma più facile da usare, poiché è richiamabile dalla classe di stringhe.

Lo scanner è arrivato su JDK 1.5. È il più flessibile e colma un divario di lunga data sull'API Java per supportare un equivalente della famosa famiglia di funzioni scanf CS.

La divisione è lenta, ma non lenta come lo scanner. StringTokenizer è più veloce di Spalato. Tuttavia, ho scoperto che avrei potuto ottenere il doppio della velocità, scambiando una certa flessibilità, per ottenere una velocità di velocità, cosa che ho fatto a JfastParser https://github.com/hughperkins/jfastparser

Test su una stringa contenente un milione di doppi:

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

Se si dispone di un oggetto stringa si desidera tokenizzare, favorisce l'utilizzo di String diviso Metodo su un stringtokenizer. Se stai analizzando i dati di testo da una fonte al di fuori del programma, come da un file o dall'utente, è qui che uno scanner è utile.

String.split sembra essere molto più lento di StringTokenizer. L'unico vantaggio con la divisione è che si ottiene una serie di token. Inoltre è possibile utilizzare eventuali espressioni regolari nella divisione. org.apache.commons.lang.stringutils ha un metodo diviso che funziona molto più velocemente di qualsiasi due viz. StringTokenizer o String.split. Ma l'utilizzo della CPU per tutti e tre è quasi lo stesso. Quindi abbiamo anche bisogno di un metodo che è meno intensivo della CPU, che non sono ancora in grado di trovare.

Di recente ho fatto alcuni esperimenti sulle cattive prestazioni di String.split () in situazioni altamente sensibili alle prestazioni. Potresti trovarlo utile.

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

Il GIST è quella string.split () compila ogni volta un modello di espressione regolare e può quindi rallentare il programma, rispetto a se si utilizza un oggetto modello precompilato e lo usi direttamente per funzionare su una stringa.

Per gli scenari predefiniti suggerirei anche pattern.split () ma se hai bisogno di massime prestazioni (specialmente su Android tutte le soluzioni che ho testato sono piuttosto lente) e devi solo divisi in un singolo carattere, ora uso il mio metodo:

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;
}

Usa "ABC" .toChararray () per ottenere l'array char per una stringa. Per esempio:

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

Una differenza importante è che sia String.split () che lo scanner possono produrre stringhe vuote ma StringTokenizer non lo fa mai.

Per esempio:

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());

Produzione:

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

Questo perché il delimitatore per String.split () e scanner.usedelimiter () non è solo una stringa, ma un'espressione regolare. Possiamo sostituire il delimitatore "" con " +" nell'esempio sopra per farli comportarsi come StringTokenizer.

String.split () funziona molto bene ma ha i suoi confini, come se volessi dividere una stringa come mostrato di seguito in base al simbolo a tubo singolo o doppio (|), non funziona. In questa situazione puoi usare StringTokenizer.

ABC | ijk

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top