Consulte también stackoverflow.com/questions/8894258/… Los puntos de referencia muestran que String.charAt () es el más rápido para cadenas pequeñas, y el uso de la reflexión para leer el conjunto de caracteres directamente es más rápido para cadenas grandes.
Utilizo un bucle for para iterar la cadena y utilizo charAt()para que cada personaje lo examine. Como la cadena se implementa con una matriz, el charAt()método es una operación de tiempo constante.
String s ="...stuff...";for(int i =0; i < s.length(); i++){char c = s.charAt(i);//Process char}
Eso es lo que yo haría. Me parece lo más fácil.
En lo que respecta a la corrección, no creo que exista aquí. Todo se basa en tu estilo personal.
podría en línea length (), es decir, izar el método detrás de esa llamada unos pocos cuadros, pero es más eficiente hacer esto para (int i = 0, n = s.length (); i <n; i ++) {char c = s.charAt (i); }
Dave Cheney el
32
Desordenar su código para una pequeña ganancia de rendimiento. Evite esto hasta que decida que esta área de código es crítica para la velocidad.
delgado
31
Tenga en cuenta que esta técnica le proporciona caracteres , no puntos de código , lo que significa que puede obtener sustitutos.
Gabe
2
@ikh charAt no es O (1) : ¿Cómo es eso? El código para String.charAt(int)es simplemente hacer value[index]. Creo que estás confundiendo chatAt()con algo más que te da puntos de código.
antak
209
Dos opciones
for(int i =0, n = s.length(); i < n ; i++){char c = s.charAt(i);}
o
for(char c : s.toCharArray()){// process c}
El primero es probablemente más rápido, luego el segundo es probablemente más legible.
más uno para colocar s.length () en la expresión de inicialización. Si alguien no sabe por qué, es porque eso solo se evalúa una vez donde si se colocó en la declaración de terminación como i <s.length (), entonces se llamaría s.length () cada vez que se repite.
Dennis
57
Pensé que la optimización del compilador se encargó de eso por ti.
Rhyous
44
@Matthias Puede usar el desensamblador de clases Javap para ver que se evitan las llamadas repetidas a s.length () en la expresión de terminación de bucle. Tenga en cuenta que en el código OP publicado, la llamada a s.length () está en la expresión de inicialización, por lo que la semántica del lenguaje ya garantiza que se invocará solo una vez.
prasopes
3
@prasopes Sin embargo, tenga en cuenta que la mayoría de las optimizaciones de Java ocurren en el tiempo de ejecución, NO en los archivos de clase. Incluso si vio repetidas llamadas a length () que no indica una penalización de tiempo de ejecución, necesariamente.
Isaac
2
@Lasse, la supuesta razón es la eficiencia: su versión llama al método length () en cada iteración, mientras que Dave lo llama una vez en el inicializador. Dicho esto, es muy probable que el optimizador JIT ("justo a tiempo") optimice la llamada adicional, por lo que es probable que solo sea una diferencia de legibilidad sin ganancia real.
Steve
90
Tenga en cuenta que la mayoría de las otras técnicas descritas aquí se desglosan si se trata de caracteres fuera del BMP ( Plano multilingüe básico Unicode ), es decir , puntos de código que están fuera del rango u0000-uFFFF. Esto solo ocurrirá en raras ocasiones, ya que los puntos de código fuera de esto se asignan principalmente a idiomas muertos. Pero hay algunos caracteres útiles fuera de esto, por ejemplo, algunos puntos de código utilizados para la notación matemática, y algunos utilizados para codificar nombres propios en chino.
En ese caso su código será:
String str ="....";int offset =0, strLen = str.length();while(offset < strLen){int curChar = str.codePointAt(offset);
offset +=Character.charCount(curChar);// do something with curChar}
El Character.charCount(int)método requiere Java 5+.
No entiendo cómo usas nada más que el plano multilingüe básico aquí. curChar todavía tiene 16 bits?
contrato del Prof. Falken incumplió
2
Puede usar un int para almacenar el punto de código completo o bien, cada carácter solo almacenará uno de los dos pares sustitutos que definen el punto de código.
sk.
1
Creo que necesito leer sobre puntos de código y pares sustitutos. ¡Gracias!
contrato del Prof. Falken incumplió
66
+1 ya que esta parece ser la única respuesta correcta para caracteres Unicode fuera del BMP
Jason S
Escribió un código para ilustrar el concepto de iterar sobre puntos de código (en lugar de caracteres): gist.github.com/EmmanuelOga/…
Emmanuel Oga
26
Estoy de acuerdo en que StringTokenizer es excesivo aquí. En realidad probé las sugerencias anteriores y me tomé el tiempo.
Mi prueba fue bastante simple: crear un StringBuilder con aproximadamente un millón de caracteres, convertirlo en una Cadena y atravesar cada uno de ellos con charAt () / después de convertirlo en una matriz de caracteres / con un CharacterIterator mil veces (por supuesto asegurándose de hacer algo en la cadena para que el compilador no pueda optimizar todo el ciclo :-)).
El resultado en mi Powerbook de 2.6 GHz (eso es un mac :-)) y JDK 1.5:
Prueba 1: charAt + String -> 3138msec
Prueba 2: Cadena convertida a matriz -> 9568mseg
Prueba 3: Charing StringBuilder -> 3536mseg
Prueba 4: CharacterIterator y String -> 12151msec
Como los resultados son significativamente diferentes, la forma más directa también parece ser la más rápida. Curiosamente, charAt () de un StringBuilder parece ser un poco más lento que el de String.
Por cierto, sugiero no usar CharacterIterator ya que considero que su abuso del carácter '\ uFFFF' como "final de la iteración" es un truco realmente horrible. En los grandes proyectos siempre hay dos tipos que usan el mismo tipo de pirateo para dos propósitos diferentes y el código se bloquea realmente misteriosamente.
Aquí hay una de las pruebas:
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");
El método chars () devuelve un IntStreamcomo se menciona en doc :
Devuelve una secuencia de int cero que extiende los valores de caracteres de esta secuencia. Cualquier carácter que se asigne a un punto de código sustituto se pasa sin interpretación. Si la secuencia está mutada mientras se lee la secuencia, el resultado no está definido.
El método codePoints()también devuelve un IntStreamsegún el documento:
Devuelve una secuencia de valores de puntos de código de esta secuencia. Cualquier par sustituto encontrado en la secuencia se combina como si fuera por Character.toCodePoint y el resultado se pasa a la secuencia. Cualquier otra unidad de código, incluidos los caracteres BMP ordinarios, los sustitutos no apareados y las unidades de código indefinidas, se extienden a cero a valores int que luego se pasan a la secuencia.
¿En qué se diferencian char y code point? Como se menciona en este artículo:
Unicode 3.1 agregó caracteres suplementarios, elevando el número total de caracteres a más de 216 caracteres que se pueden distinguir por un solo 16 bits char . Por lo tanto, un charvalor ya no tiene un mapeo uno a uno a la unidad semántica fundamental en Unicode. JDK 5 se actualizó para admitir el conjunto más grande de valores de caracteres. En lugar de cambiar la definición del chartipo, algunos de los nuevos caracteres suplementarios están representados por un par sustituto de dos charvalores. Para reducir la confusión de nombres, se usará un punto de código para referirse al número que representa un carácter Unicode particular, incluidos los complementarios.
Finalmente por qué forEachOrderedy no forEach?
El comportamiento de forEaches explícitamente no determinista donde, a medida que forEachOrderedrealiza una acción para cada elemento de esta secuencia, en el orden de encuentro de la secuencia si la secuencia tiene un orden de encuentro definido. Por forEachlo tanto , no garantiza que se mantenga el pedido. También revise esta pregunta para más.
Para ver la diferencia entre un personaje, un punto de código, un glifo y un grafema, consulte esta pregunta .
import java.text.*;finalCharacterIterator it =newStringCharacterIterator(s);for(char c = it.first(); c !=CharacterIterator.DONE; c = it.next()){// process c...}
Parece una exageración para algo tan simple como iterar sobre una matriz de caracteres inmutable.
ddimitrov
1
No veo por qué esto es exagerado. Los iteradores son la forma más java-ish de hacer cualquier cosa ... iterativa. El StringCharacterIterator seguramente aprovechará al máximo la inmutabilidad.
delgado
2
De acuerdo con @ddimitrov: esto es exagerado. La única razón para usar un iterador sería aprovechar foreach, que es un poco más fácil de "ver" que un bucle for. Si va a escribir un bucle for convencional de todos modos, entonces podría usar charAt ()
Rob Gilliam
3
El uso del iterador de caracteres es probablemente la única forma correcta de iterar sobre los caracteres, porque Unicode requiere más espacio del que charproporciona Java . Un Java charcontiene 16 bits y puede contener caracteres Unicode hasta U + FFFF, pero Unicode especifica caracteres hasta U + 10FFFF. El uso de 16 bits para codificar Unicode da como resultado una codificación de caracteres de longitud variable. La mayoría de las respuestas en esta página asumen que la codificación Java es una codificación de longitud constante, lo cual es incorrecto.
Si tienes guayaba en tu classpath, la siguiente es una alternativa bastante legible. La guayaba incluso tiene una implementación de Lista personalizada bastante sensata para este caso, por lo que esto no debería ser ineficiente.
for(char c :Lists.charactersOf(yourString)){// Do whatever you want }
ACTUALIZACIÓN: Como señaló @Alex, con Java 8 también hay CharSequence#charsque usarlo. Incluso el tipo es IntStream, por lo que se puede asignar a caracteres como:
yourString.chars().mapToObj(c ->Character.valueOf((char) c)).forEach(c ->System.out.println(c));// Or whatever you want
Si necesita hacer algo complejo, vaya con el bucle for + guava ya que no puede mutar variables (por ejemplo, enteros y cadenas) definidas fuera del alcance de forEach dentro de forEach. Lo que esté dentro de forEach tampoco puede arrojar excepciones marcadas, por lo que a veces eso también es molesto.
sabujp
13
Si necesita iterar a través de los puntos de código de a String(vea esta respuesta ), una forma más corta / más legible es usar el CharSequence#codePointsmétodo agregado en Java 8:
for(int c : string.codePoints().toArray()){...}
o usando la secuencia directamente en lugar de un bucle for:
string.codePoints().forEach(c ->...);
También existe CharSequence#charssi desea una secuencia de los caracteres (aunque es un IntStream, ya que no hay CharStream).
No lo usaría, StringTokenizerya que es una de las clases en el JDK que es legado.
El javadoc dice:
StringTokenizeres una clase heredada que se conserva por razones de compatibilidad, aunque se desaconseja su uso en el nuevo código. Se recomienda que cualquiera que busque esta funcionalidad use el método dividido Stringo el
java.util.regexpaquete en su lugar.
El tokenizador de cadenas es una forma perfectamente válida (y más eficiente) para iterar sobre tokens (es decir, palabras en una oración). Definitivamente es una exageración para iterar sobre caracteres. Estoy rechazando su comentario como engañoso.
Gracias Sr. Bemrose ... Supongo que la cita de bloque citada debería haber sido clara, donde probablemente se debería inferir que las correcciones de errores activas no se comprometerán con StringTokenizer.
Alan
2
Si necesita rendimiento, debe probar en su entorno. Ninguna otra manera.
Aquí el código de ejemplo:
int tmp =0;String s =newString(newbyte[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);
publicclassStringDemo{publicstaticvoid main(String[] args){String palindrome ="Dot saw I was Tod";int len = palindrome.length();char[] tempCharArray =newchar[len];char[] charArray =newchar[len];// put original string in an array of charsfor(int i =0; i < len; i++){
tempCharArray[i]= palindrome.charAt(i);}// reverse array of charsfor(int j =0; j < len; j++){
charArray[j]= tempCharArray[len -1- j];}String reversePalindrome =newString(charArray);System.out.println(reversePalindrome);}}
Estoy empezando a sentirme un poco spammer ... si existe esa palabra :). Pero esta solución también tiene el problema descrito aquí: tiene el mismo problema descrito aquí: stackoverflow.com/questions/196830/…
Emmanuel Oga
0
StringTokenizer es totalmente inadecuado para la tarea de dividir una cadena en sus caracteres individuales. Con String#split()usted puede hacerlo fácilmente utilizando una expresión regular que no coincida con nada, por ejemplo:
String[] theChars = str.split("|");
Pero StringTokenizer no usa expresiones regulares, y no hay una cadena delimitadora que pueda especificar que no coincida con la nada entre los caracteres. No es un lindo truco que puede utilizar para lograr lo mismo: utilizar la cadena como la cadena delimitadora (haciendo todos los personajes en un delimitador) y tienen que devolver los delimitadores:
StringTokenizer st =newStringTokenizer(str, str,true);
Sin embargo, solo menciono estas opciones con el propósito de descartarlas. Ambas técnicas dividen la cadena original en cadenas de un carácter en lugar de primitivas de caracteres, y ambas implican una gran sobrecarga en forma de creación de objetos y manipulación de cadenas. Compare eso con llamar a charAt () en un bucle for, que prácticamente no genera gastos generales.
Las respuestas anteriores señalan el problema de muchas de las soluciones aquí que no iteran por el valor del punto de código: tendrían problemas con cualquier carácter sustituto . Los documentos de Java también describen el problema aquí (consulte "Representaciones de caracteres Unicode"). De todos modos, aquí hay un código que usa algunos caracteres sustitutos reales del conjunto Unicode suplementario, y los convierte de nuevo en una Cadena. Tenga en cuenta que .toChars () devuelve una serie de caracteres: si se trata de sustitutos, necesariamente tendrá dos caracteres. Este código debería funcionar para cualquier carácter Unicode.
Por lo general, hay dos formas de iterar a través de una cadena en java que ya ha sido respondida por varias personas aquí en este hilo, solo agregando mi versión de él Primero está usando
String s = sc.next()// assuming scanner class is defined abovefor(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 =newchar[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
Si el rendimiento está en juego, recomendaré usar el primero en tiempo constante, si no es así, ir con el segundo hace que su trabajo sea más fácil teniendo en cuenta la inmutabilidad con las clases de cadena en java.
Respuestas:
Utilizo un bucle for para iterar la cadena y utilizo
charAt()
para que cada personaje lo examine. Como la cadena se implementa con una matriz, elcharAt()
método es una operación de tiempo constante.Eso es lo que yo haría. Me parece lo más fácil.
En lo que respecta a la corrección, no creo que exista aquí. Todo se basa en tu estilo personal.
fuente
String.charAt(int)
es simplemente hacervalue[index]
. Creo que estás confundiendochatAt()
con algo más que te da puntos de código.Dos opciones
o
El primero es probablemente más rápido, luego el segundo es probablemente más legible.
fuente
Tenga en cuenta que la mayoría de las otras técnicas descritas aquí se desglosan si se trata de caracteres fuera del BMP ( Plano multilingüe básico Unicode ), es decir , puntos de código que están fuera del rango u0000-uFFFF. Esto solo ocurrirá en raras ocasiones, ya que los puntos de código fuera de esto se asignan principalmente a idiomas muertos. Pero hay algunos caracteres útiles fuera de esto, por ejemplo, algunos puntos de código utilizados para la notación matemática, y algunos utilizados para codificar nombres propios en chino.
En ese caso su código será:
El
Character.charCount(int)
método requiere Java 5+.Fuente: http://mindprod.com/jgloss/codepoint.html
fuente
Estoy de acuerdo en que StringTokenizer es excesivo aquí. En realidad probé las sugerencias anteriores y me tomé el tiempo.
Mi prueba fue bastante simple: crear un StringBuilder con aproximadamente un millón de caracteres, convertirlo en una Cadena y atravesar cada uno de ellos con charAt () / después de convertirlo en una matriz de caracteres / con un CharacterIterator mil veces (por supuesto asegurándose de hacer algo en la cadena para que el compilador no pueda optimizar todo el ciclo :-)).
El resultado en mi Powerbook de 2.6 GHz (eso es un mac :-)) y JDK 1.5:
Como los resultados son significativamente diferentes, la forma más directa también parece ser la más rápida. Curiosamente, charAt () de un StringBuilder parece ser un poco más lento que el de String.
Por cierto, sugiero no usar CharacterIterator ya que considero que su abuso del carácter '\ uFFFF' como "final de la iteración" es un truco realmente horrible. En los grandes proyectos siempre hay dos tipos que usan el mismo tipo de pirateo para dos propósitos diferentes y el código se bloquea realmente misteriosamente.
Aquí hay una de las pruebas:
fuente
En Java 8 podemos resolverlo como:
El método chars () devuelve un
IntStream
como se menciona en doc :El método
codePoints()
también devuelve unIntStream
según el documento:¿En qué se diferencian char y code point? Como se menciona en este artículo:
Finalmente por qué
forEachOrdered
y noforEach
?El comportamiento de
forEach
es explícitamente no determinista donde, a medida queforEachOrdered
realiza una acción para cada elemento de esta secuencia, en el orden de encuentro de la secuencia si la secuencia tiene un orden de encuentro definido. PorforEach
lo tanto , no garantiza que se mantenga el pedido. También revise esta pregunta para más.Para ver la diferencia entre un personaje, un punto de código, un glifo y un grafema, consulte esta pregunta .
fuente
Hay algunas clases dedicadas para esto:
fuente
char
proporciona Java . Un Javachar
contiene 16 bits y puede contener caracteres Unicode hasta U + FFFF, pero Unicode especifica caracteres hasta U + 10FFFF. El uso de 16 bits para codificar Unicode da como resultado una codificación de caracteres de longitud variable. La mayoría de las respuestas en esta página asumen que la codificación Java es una codificación de longitud constante, lo cual es incorrecto.Si tienes guayaba en tu classpath, la siguiente es una alternativa bastante legible. La guayaba incluso tiene una implementación de Lista personalizada bastante sensata para este caso, por lo que esto no debería ser ineficiente.
ACTUALIZACIÓN: Como señaló @Alex, con Java 8 también hay
CharSequence#chars
que usarlo. Incluso el tipo es IntStream, por lo que se puede asignar a caracteres como:fuente
Si necesita iterar a través de los puntos de código de a
String
(vea esta respuesta ), una forma más corta / más legible es usar elCharSequence#codePoints
método agregado en Java 8:o usando la secuencia directamente en lugar de un bucle for:
También existe
CharSequence#chars
si desea una secuencia de los caracteres (aunque es unIntStream
, ya que no hayCharStream
).fuente
No lo usaría,
StringTokenizer
ya que es una de las clases en el JDK que es legado.El javadoc dice:
fuente
Si necesita rendimiento, debe probar en su entorno. Ninguna otra manera.
Aquí el código de ejemplo:
En Java en línea obtengo:
En Android x86 API 17 obtengo:
fuente
Consulte los Tutoriales de Java: cadenas .
Ponga la longitud
int len
y use elfor
bucle.fuente
StringTokenizer es totalmente inadecuado para la tarea de dividir una cadena en sus caracteres individuales. Con
String#split()
usted puede hacerlo fácilmente utilizando una expresión regular que no coincida con nada, por ejemplo:Pero StringTokenizer no usa expresiones regulares, y no hay una cadena delimitadora que pueda especificar que no coincida con la nada entre los caracteres. No es un lindo truco que puede utilizar para lograr lo mismo: utilizar la cadena como la cadena delimitadora (haciendo todos los personajes en un delimitador) y tienen que devolver los delimitadores:
Sin embargo, solo menciono estas opciones con el propósito de descartarlas. Ambas técnicas dividen la cadena original en cadenas de un carácter en lugar de primitivas de caracteres, y ambas implican una gran sobrecarga en forma de creación de objetos y manipulación de cadenas. Compare eso con llamar a charAt () en un bucle for, que prácticamente no genera gastos generales.
fuente
Elaborando sobre esta respuesta y esta respuesta .
Las respuestas anteriores señalan el problema de muchas de las soluciones aquí que no iteran por el valor del punto de código: tendrían problemas con cualquier carácter sustituto . Los documentos de Java también describen el problema aquí (consulte "Representaciones de caracteres Unicode"). De todos modos, aquí hay un código que usa algunos caracteres sustitutos reales del conjunto Unicode suplementario, y los convierte de nuevo en una Cadena. Tenga en cuenta que .toChars () devuelve una serie de caracteres: si se trata de sustitutos, necesariamente tendrá dos caracteres. Este código debería funcionar para cualquier carácter Unicode.
fuente
¡Este código de ejemplo te ayudará!
fuente
Por lo general, hay dos formas de iterar a través de una cadena en java que ya ha sido respondida por varias personas aquí en este hilo, solo agregando mi versión de él Primero está usando
Si el rendimiento está en juego, recomendaré usar el primero en tiempo constante, si no es así, ir con el segundo hace que su trabajo sea más fácil teniendo en cuenta la inmutabilidad con las clases de cadena en java.
fuente