StringTokenizer
? Convertire il String
in a char[]
e iterare su quello? Qualcos'altro?
StringTokenizer
? Convertire il String
in a char[]
e iterare su quello? Qualcos'altro?
Risposte:
Uso un ciclo for per iterare la stringa e uso charAt()
per far esaminare ogni personaggio da ogni personaggio. Poiché la stringa è implementata con un array, il charAt()
metodo è un'operazione a tempo costante.
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
Questo è quello che vorrei fare. Mi sembra il più semplice.
Per quanto riguarda la correttezza, non credo che esista qui. È tutto basato sul tuo stile personale.
String.charAt(int)
sta semplicemente facendo value[index]
. Penso che tu sia confuso chatAt()
con qualcos'altro che ti dà punti di codice.
Due opzioni
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
o
for(char c : s.toCharArray()) {
// process c
}
Il primo è probabilmente più veloce, quindi il secondo è probabilmente più leggibile.
Nota che la maggior parte delle altre tecniche qui descritte si interrompe se hai a che fare con caratteri al di fuori del BMP (Unicode Basic Multilingual Plane ), ovvero punti di codice che sono al di fuori dell'intervallo u0000-uFFFF. Questo accadrà solo raramente, poiché i punti di codice al di fuori di questo sono per lo più assegnati a lingue morte. Ma ci sono alcuni caratteri utili al di fuori di questo, ad esempio alcuni punti di codice usati per la notazione matematica e alcuni usati per codificare nomi propri in cinese.
In tal caso il tuo codice sarà:
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
Il Character.charCount(int)
metodo richiede Java 5+.
Sono d'accordo che StringTokenizer è eccessivo qui. In realtà ho provato i suggerimenti sopra e mi sono preso il tempo.
Il mio test è stato abbastanza semplice: creare un StringBuilder con circa un milione di caratteri, convertirlo in una stringa e attraversare ciascuno di essi con charAt () / dopo la conversione in un array di caratteri / con un CharacterIterator mille volte (ovviamente assicurandosi di fai qualcosa sulla stringa in modo che il compilatore non possa ottimizzare l'intero ciclo :-)).
Il risultato sul mio Powerbook a 2,6 GHz (che è un mac :-)) e JDK 1.5:
Poiché i risultati sono significativamente diversi, anche il modo più semplice sembra essere il più veloce. È interessante notare che charAt () di StringBuilder sembra essere leggermente più lento di quello di String.
A proposito, suggerisco di non usare CharacterIterator poiché considero il suo abuso del carattere '\ uFFFF' come "fine dell'iterazione" un hack davvero terribile. Nei grandi progetti ci sono sempre due ragazzi che usano lo stesso tipo di hack per due scopi diversi e il codice si arresta in modo davvero misterioso.
Ecco uno dei test:
int count = 1000;
...
System.out.println("Test 1: charAt + String");
long t = System.currentTimeMillis();
int sum=0;
for (int i=0; i<count; i++) {
int len = str.length();
for (int j=0; j<len; j++) {
if (str.charAt(j) == 'b')
sum = sum + 1;
}
}
t = System.currentTimeMillis()-t;
System.out.println("result: "+ sum + " after " + t + "msec");
In Java 8 possiamo risolverlo come:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
Il metodo chars () restituisce un IntStream
come menzionato nel doc :
Restituisce un flusso di int zero che estende i valori del carattere da questa sequenza. Qualsiasi carattere associato a un punto di codice surrogato viene passato senza interpretazione. Se la sequenza viene modificata durante la lettura dello stream, il risultato non è definito.
Il metodo codePoints()
restituisce anche un IntStream
secondo documento:
Restituisce un flusso di valori di punti di codice da questa sequenza. Tutte le coppie surrogate incontrate nella sequenza vengono combinate come da Character.toCodePoint e il risultato viene passato allo stream. Qualsiasi altra unità di codice, inclusi i normali caratteri BMP, surrogati non accoppiati e unità di codice non definite, viene estesa a zero ai valori int che vengono quindi passati allo stream.
In cosa differiscono char e code point? Come menzionato in questo articolo:
Unicode 3.1 ha aggiunto caratteri supplementari, portando il numero totale di caratteri a oltre i 216 caratteri che possono essere distinti da un singolo 16 bit
char
. Pertanto, unchar
valore non ha più un mapping uno a uno all'unità semantica fondamentale in Unicode. JDK 5 è stato aggiornato per supportare il set più ampio di valori di carattere. Invece di modificare la definizione delchar
tipo, alcuni dei nuovi caratteri supplementari sono rappresentati da una coppia surrogata di duechar
valori. Per ridurre la confusione dei nomi, verrà utilizzato un punto di codice per fare riferimento al numero che rappresenta un particolare carattere Unicode, inclusi quelli supplementari.
Finalmente perché forEachOrdered
e no forEach
?
Il comportamento di forEach
è esplicitamente non deterministico dove come forEachOrdered
esegue un'azione per ciascun elemento di questo flusso, nell'ordine di incontro del flusso se il flusso ha un ordine di incontro definito. Quindi forEach
non garantisce che l'ordine verrà mantenuto. Controlla anche questa domanda per ulteriori informazioni.
Per la differenza tra un carattere, un punto di codice, un glifo e un grafema controlla questa domanda .
Ci sono alcune lezioni dedicate per questo:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
fornito da Java . Un Java char
contiene 16 bit e può contenere caratteri Unicode su U + FFFF ma Unicode specifica caratteri fino a U + 10FFFF. L'uso di 16 bit per codificare Unicode produce una codifica di caratteri a lunghezza variabile. La maggior parte delle risposte in questa pagina presuppone che la codifica Java sia una codifica di lunghezza costante, che è errata.
Se hai Guava sul tuo percorso di classe, la seguente è un'alternativa piuttosto leggibile. Guava ha anche un'implementazione dell'elenco personalizzata abbastanza ragionevole per questo caso, quindi questo non dovrebbe essere inefficiente.
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
AGGIORNAMENTO: Come notato da @Alex, con Java 8 c'è anche CharSequence#chars
da usare. Anche il tipo è IntStream, quindi può essere mappato su caratteri come:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
Se è necessario scorrere i punti di codice di un String
(vedere questa risposta ), un modo più breve / più leggibile consiste nell'utilizzare il CharSequence#codePoints
metodo aggiunto in Java 8:
for(int c : string.codePoints().toArray()){
...
}
o usando direttamente lo stream invece di un ciclo for:
string.codePoints().forEach(c -> ...);
C'è anche CharSequence#chars
se vuoi un flusso di personaggi (anche se è un IntStream
, poiché non c'è CharStream
).
Non userei StringTokenizer
perché è una delle classi del JDK che è legacy.
Il javadoc dice:
StringTokenizer
è una classe legacy che viene conservata per motivi di compatibilità sebbene il suo utilizzo sia sconsigliato nel nuovo codice. Si consiglia a chiunque cerchi questa funzionalità di utilizzare invece il metodo splitString
o iljava.util.regex
pacchetto.
Se hai bisogno di prestazioni, devi testare sul tuo ambiente. Nessun altro modo.
Ecco un esempio di codice:
int tmp = 0;
String s = new String(new byte[64*1024]);
{
long st = System.nanoTime();
for(int i = 0, n = s.length(); i < n; i++) {
tmp += s.charAt(i);
}
st = System.nanoTime() - st;
System.out.println("1 " + st);
}
{
long st = System.nanoTime();
char[] ch = s.toCharArray();
for(int i = 0, n = ch.length; i < n; i++) {
tmp += ch[i];
}
st = System.nanoTime() - st;
System.out.println("2 " + st);
}
{
long st = System.nanoTime();
for(char c : s.toCharArray()) {
tmp += c;
}
st = System.nanoTime() - st;
System.out.println("3 " + st);
}
System.out.println("" + tmp);
Su Java online ottengo:
1 10349420
2 526130
3 484200
0
Su Android x86 API 17 ottengo:
1 9122107
2 13486911
3 12700778
0
Vedi Tutorial Java: Stringhe .
public class StringDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
char[] tempCharArray = new char[len];
char[] charArray = new char[len];
// put original string in an array of chars
for (int i = 0; i < len; i++) {
tempCharArray[i] = palindrome.charAt(i);
}
// reverse array of chars
for (int j = 0; j < len; j++) {
charArray[j] = tempCharArray[len - 1 - j];
}
String reversePalindrome = new String(charArray);
System.out.println(reversePalindrome);
}
}
Inserisci la lunghezza int len
e usa il for
loop.
StringTokenizer è totalmente inadatto al compito di spezzare una stringa nei suoi singoli caratteri. Con String#split()
te puoi farlo facilmente usando un regex che non corrisponde a nulla, ad esempio:
String[] theChars = str.split("|");
Ma StringTokenizer non usa regex e non esiste una stringa del delimitatore che puoi specificare che non corrisponderà al nulla tra i caratteri. V'è un poco carino incidere è possibile utilizzare per realizzare la stessa cosa: utilizzare la stringa stessa come delimitatore per la stringa (rendendo ogni personaggio in esso un delimitatore) e farlo tornare i delimitatori:
StringTokenizer st = new StringTokenizer(str, str, true);
Tuttavia, cito solo queste opzioni allo scopo di respingerle. Entrambe le tecniche suddividono la stringa originale in stringhe di un carattere anziché in caratteri primitivi, ed entrambe comportano un notevole sovraccarico sotto forma di creazione di oggetti e manipolazione di stringhe. Confrontalo con la chiamata a charAt () in un ciclo for, che non comporta praticamente alcun sovraccarico.
Elaborazione di questa risposta e questa risposta .
Le risposte sopra evidenziano il problema di molte delle soluzioni qui che non ripetono il valore del punto di codice: avrebbero problemi con qualsiasi carattere surrogato . Anche i documenti java delineano qui il problema (vedi "Rappresentazioni dei caratteri Unicode"). In ogni caso, ecco qualche codice che utilizza alcuni caratteri surrogati reale dal set Unicode supplementare, e li converte indietro in una stringa. Nota che .toChars () restituisce una matrice di caratteri: se hai a che fare con surrogati, avrai necessariamente due caratteri. Questo codice dovrebbe funzionare per qualsiasi carattere Unicode.
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
Questo codice di esempio ti aiuterà!
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Solution {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 10);
map.put("b", 30);
map.put("c", 50);
map.put("d", 40);
map.put("e", 20);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
Comparable valueA = (Comparable) map.get(keyA);
Comparable valueB = (Comparable) map.get(keyB);
return valueB.compareTo(valueA);
}
}
Quindi in genere ci sono due modi per iterare attraverso la stringa in Java che ha già ricevuto risposta da più persone qui in questo thread, solo aggiungendo la mia versione di essa per prima cosa sta usando
String s = sc.next() // assuming scanner class is defined above
for(int i=0; i<s.length; i++){
s.charAt(i) // This being the first way and is a constant time operation will hardly add any overhead
}
char[] str = new char[10];
str = s.toCharArray() // this is another way of doing so and it takes O(n) amount of time for copying contents from your string class to character array
Se la prestazione è in gioco, allora consiglierò di usare il primo a tempo costante, se non lo è con il secondo, semplifica il tuo lavoro considerando l'immutabilità con le classi di stringhe in Java.