¿Cuál es la forma más fácil / mejor / más correcta de iterar a través de los caracteres de una cadena en Java?

StringTokenizer ? Convierte el String en un char[] e itera sobre eso? ¿Algo más?

Uso un ciclo for para iterar la cadena y uso charAt() para que cada personaje lo examine. Dado que String se implementa con una matriz, el método charAt() 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. Parece lo más fácil para mí.

En cuanto a la corrección, no creo que exista aquí. Todo se basa en tu estilo personal.

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.

Tenga en cuenta que la mayoría de las otras técnicas descritas aquí se descomponen si se trata de caracteres fuera del BMP ( plano multilingüe básico de 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 que están fuera de este 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 método Character.charCount(int) requiere Java 5+.

Fuente: http://mindprod.com/jgloss/codepoint.html

Estoy de acuerdo en que StringTokenizer es excesivo aquí. De hecho, probé las sugerencias anteriores y me tomé el tiempo.

Mi prueba fue bastante simple: crear un StringBuilder con alrededor de un millón de caracteres, convertirlo a String, y recorrer cada uno de ellos con charAt () / después de convertir a una matriz de caracteres / con un CharacterIterator mil veces (por supuesto, asegúrese de hacer algo en la cadena para que el comstackdor no pueda optimizar todo el ciclo :-)).

El resultado en mi Powerbook de 2.6 GHz (que es un mac :-)) y JDK 1.5:

  • Prueba 1: charAt + String -> 3138msec
  • Prueba 2: cadena convertida a matriz -> 9568 mseg
  • Prueba 3: StringBuilder charAt -> 3536msec
  • Prueba 4: CharacterIterator y cadena -> 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 “fin de iteración” es un truco realmente horrible. En los grandes proyectos siempre hay dos tipos que usan el mismo tipo de hack para dos propósitos diferentes y el código se bloquea de forma misteriosa.

Aquí está 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 

Hay algunas clases dedicadas para esto:

 import java.text.*; final CharacterIterator it = new StringCharacterIterator(s); for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) { // process c ... } 

Si tienes Guava en tu classpath, la siguiente es una alternativa bastante legible. Guava 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 CharSequence#chars para usar. 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 iterar a través de los puntos de código de un String (vea esta respuesta ), una forma más corta / más legible es usar el método CharSequence#codePoints agregado en Java 8:

 for(int c : string.codePoints().toArray()){ ... } 

o usando la secuencia directamente en lugar de un ciclo for:

 string.codePoints().forEach(c -> ...); 

También hay CharSequence#chars si desea una secuencia de caracteres (aunque es un IntStream , ya que no hay CharStream ).

En Java 8 podemos resolverlo como:

 String str = "xyz"; str.chars().forEachOrdered(i -> System.out.print((char)i)); str.codePoints().forEachOrdered(i -> System.out.print((char)i)); 

El método chars () devuelve un IntStream como se menciona en el documento :

Devuelve una secuencia de int extendiendo los valores de char de esta secuencia. Cualquier char que se asigna a un punto de código sustituto se pasa sin ser interpretado. Si la secuencia se muta mientras se lee la secuencia, el resultado no está definido.

El método codePoints() también devuelve un IntStream según el documento:

Devuelve una secuencia de valores de puntos de código de esta secuencia. Cualquier pareja sustituta encontrada en la secuencia se combina como si fuera Character.toCodePoint y el resultado se pasa a la secuencia. Cualquier otra unidad de código, incluidos los caracteres ordinarios BMP, sustitutos sin parear y unidades de código indefinido, se extienden por cero a los valores int que luego se pasan a la secuencia.

¿Cómo es diferente el punto de código y el carácter? Como se menciona en este artículo:

Unicode 3.1 agregó caracteres adicionales, lo que aumenta el número total de caracteres a más de los 216 caracteres que se pueden distinguir por un solo carácter de 16 bits. Por lo tanto, un valor char ya no tiene una asignación de 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 tipo de caracteres, algunos de los nuevos caracteres suplementarios están representados por un par sustituto de dos valores de caracteres. Para reducir la confusión de nombres, se usará un punto de código para referirse al número que representa un determinado carácter Unicode, incluidos los adicionales.

Finalmente, ¿ forEachOrdered qué forEachOrdered y no forEach ?

El comportamiento de forEach es explícitamente no determinista cuando, como forEachOrdered realiza una acción para cada elemento de esta secuencia, en el orden de encuentro de la secuencia si la secuencia tiene una orden de encuentro definida. Entonces, forEach no garantiza que la orden se mantendrá. También verifique esta pregunta para más.

Para la diferencia entre un personaje, un punto de código, un glifo y un grafema, compruebe esta pregunta .

No usaría StringTokenizer ya que es una de las clases en el JDK heredada.

El javadoc dice:

StringTokenizer es 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 de división de String o el paquete java.util.regex lugar.

Ver los tutoriales de Java: cadenas .

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

Pon la longitud en int len y usa for loop.

StringTokenizer es totalmente inadecuado para la tarea de dividir una cadena en sus caracteres individuales. Con String#split() puedes hacerlo fácilmente usando una expresión regular que no concuerde con nada, por ejemplo:

 String[] theChars = str.split("|"); 

Pero StringTokenizer no usa expresiones regulares, y no hay una cadena delimitadora que pueda especificar que coincida con la nada entre caracteres. Hay un pequeño truco lindo que puede usar para lograr lo mismo: usar la cadena misma como cadena delimitadora (haciendo que cada carácter en ella sea un delimitador) y hacer que devuelva los delimitadores:

 StringTokenizer st = new StringTokenizer(str, str, true); 

Sin embargo, solo menciono estas opciones con el propósito de descartarlas. Ambas técnicas rompen la cadena original en cadenas de un carácter en lugar de primitivas de caracteres, y ambas implican una gran cantidad de sobrecarga en forma de creación de objetos y manipulación de cadenas. Compare eso para llamar a charAt () en un bucle for, que no incurre prácticamente en gastos generales.

Elaborando sobre esta respuesta y esta respuesta .

Las respuestas anteriores señalan el problema de muchas de las soluciones aquí que no se repiten por el valor del punto de código: tendrían problemas con los caracteres sustitutos . Los documentos de Java también describen el problema aquí (ver “Representaciones de caracteres Unicode”). De todos modos, aquí hay un código que usa algunos caracteres reales de sustitución del conjunto Unicode suplementario y los convierte de nuevo en una Cadena. Tenga en cuenta que .toChars () devuelve una matriz de caracteres: si está tratando con sustitutos, necesariamente tendrá dos caracteres. Este código debería funcionar para cualquier carácter Unicode.

  String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓"; supplementary.codePoints().forEach(cp -> System.out.print(new String(Character.toChars(cp)))); 

¡Este código de ejemplo te ayudará!

 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 map = new HashMap(); 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); } }