una separar recorrer por para imprimir funcion caracteres caracter cadenas cadena buscar java string performance char iteration

separar - recorrer un string javascript



La forma más rápida de iterar sobre todos los caracteres en una cadena (9)

En Java, ¿cuál sería la forma más rápida de iterar sobre todos los caracteres en una Cadena, esto:

String str = "a really, really long string"; for (int i = 0, n = str.length(); i < n; i++) { char c = str.charAt(i); }

O esto:

char[] chars = str.toCharArray(); for (int i = 0, n = chars.length; i < n; i++) { char c = chars[i]; }

EDITAR:

Lo que me gustaría saber es si el costo de llamar repetidamente al método charAt durante una iteración larga termina siendo menor o mayor que el costo de realizar una única llamada toCharArray al principio y luego acceder directamente a la matriz durante el iteración.

Sería genial si alguien pudiera proporcionar un punto de referencia robusto para diferentes longitudes de cadena, teniendo en cuenta el tiempo de calentamiento JIT, el tiempo de inicio de la JVM, etc. y no solo la diferencia entre dos llamadas a System.currentTimeMillis() .


A pesar de la respuesta de @Saint Hill si considera la complejidad de tiempo de str.toCharArray () ,

el primero es más rápido incluso para cuerdas muy grandes. Puede ejecutar el código a continuación para verlo usted mismo.

char [] ch = new char[1_000_000_00]; String str = new String(ch); // to create a large string // ---> from here long currentTime = System.nanoTime(); for (int i = 0, n = str.length(); i < n; i++) { char c = str.charAt(i); } // ---> to here System.out.println("str.charAt(i):"+(System.nanoTime()-currentTime)/1000000.0 +" (ms)"); /** * ch = str.toCharArray() itself takes lots of time */ // ---> from here currentTime = System.nanoTime(); ch = str.toCharArray(); for (int i = 0, n = str.length(); i < n; i++) { char c = ch[i]; } // ---> to here System.out.println("ch = str.toCharArray() + c = ch[i] :"+(System.nanoTime()-currentTime)/1000000.0 +" (ms)");

salida:

str.charAt(i):5.492102 (ms) ch = str.toCharArray() + c = ch[i] :79.400064 (ms)


El primero que use str.charAt debería ser más rápido.

Si busca dentro del código fuente de String clase String , podemos ver que charAt se implementa de la siguiente manera:

public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; }

Aquí, todo lo que hace es indexar una matriz y devolver el valor.

Ahora, si vemos la implementación de toCharArray , encontraremos lo siguiente:

public char[] toCharArray() { char result[] = new char[count]; getChars(0, count, result, 0); return result; } public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > count) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } System.arraycopy(value, offset + srcBegin, dst, dstBegin, srcEnd - srcBegin); }

Como ve, está haciendo un System.arraycopy que definitivamente va a ser un poco más lento que no hacerlo.


El segundo hace que se cree una nueva matriz de caracteres, y todos los caracteres de cadena a copiar a esta nueva matriz de caracteres, por lo que supongo que la primera es más rápida (y tiene menos memoria).


Esto es solo una micro-optimización de la que no debes preocuparte.

char[] chars = str.toCharArray();

le devuelve una copia de matrices de caracteres str (en JDK, devuelve una copia de caracteres llamando a System.arrayCopy ).

Aparte de eso, str.charAt() solo comprueba si el índice está de hecho dentro de los límites y devuelve un carácter dentro del índice de la matriz.

El primero no crea memoria adicional en JVM.


PRIMERA ACTUALIZACIÓN: Antes de probar esto en un entorno de producción (no recomendado), lea esto primero: http://www.javaspecialists.eu/archive/Issue237.html A partir de Java 9, la solución descrita ya no funcionará. , porque ahora Java almacenará cadenas como byte [] de forma predeterminada.

SEGUNDA ACTUALIZACIÓN: A partir del 2016-10-25, en mi AMDx64 8core y fuente 1.8, no hay diferencia entre usar ''charAt'' y el acceso de campo. Parece que el jvm está lo suficientemente optimizado para alinear y simplificar cualquier llamada ''string.charAt (n)''.

Todo depende de la longitud de la String se inspecciona. Si, como dice la pregunta, es para cuerdas largas , la manera más rápida de inspeccionar la cuerda es usar la reflexión para acceder al char[] respaldo char[] de la cuerda.

Un punto de referencia totalmente aleatorizado con JDK 8 (win32 y win64) en un 64 AMD Phenom II 4 core 955 @ 3.2 GHZ (tanto en modo cliente como en modo servidor) con 9 técnicas diferentes (ver más abajo) muestra que usando String.charAt(n) es el más rápido para cadenas pequeñas y el uso de reflection para acceder a la matriz de respaldo String es casi el doble de rápido para cadenas grandes.

EL EXPERIMENTO

  • Se prueban 9 diferentes técnicas de optimización.

  • Todos los contenidos de cadenas son aleatorizados

  • La prueba se realiza para tamaños de cuerda en múltiplos de dos comenzando con 0,1,2,4,8,16, etc.

  • Las pruebas se realizan 1,000 veces por tamaño de cuerda

  • Las pruebas se mezclan en orden aleatorio cada vez. En otras palabras, las pruebas se realizan en orden aleatorio cada vez que se realizan, más de 1000 veces.

  • Todo el conjunto de pruebas se realiza hacia adelante y hacia atrás, para mostrar el efecto del calentamiento de JVM sobre la optimización y los tiempos.

  • El conjunto completo se realiza dos veces, una vez en el modo de -client y la otra en el modo de -server .

CONCLUSIONES

- modo cliente (32 bit)

Para cadenas de 1 a 256 caracteres de longitud , la llamada a string.charAt(i) gana con un procesamiento promedio de 13.4 millones a 588 millones de caracteres por segundo.

Además, es en general un 5,5% más rápido (cliente) y un 13,9% (servidor) como este:

for (int i = 0; i < data.length(); i++) { if (data.charAt(i) <= '' '') { doThrow(); } }

que así con una variable de longitud final local:

final int len = data.length(); for (int i = 0; i < len; i++) { if (data.charAt(i) <= '' '') { doThrow(); } }

Para cadenas largas, de 512 a 256K caracteres de longitud , el uso de la reflexión para acceder a la matriz de respaldo de String es más rápido. Esta técnica es casi dos veces más rápida que String.charAt (i) (178% más rápida). La velocidad promedio sobre este rango fue de 1.111 mil millones de caracteres por segundo.

El campo debe obtenerse con anticipación y luego puede reutilizarse en la biblioteca en diferentes cadenas. Curiosamente, a diferencia del código anterior, con el acceso de campo, es 9% más rápido tener una variable de longitud final local que usar ''chars.length'' en la verificación de bucle. Aquí se muestra cómo se puede configurar el acceso de campo como el más rápido:

final Field field = String.class.getDeclaredField("value"); field.setAccessible(true); try { final char[] chars = (char[]) field.get(data); final int len = chars.length; for (int i = 0; i < len; i++) { if (chars[i] <= '' '') { doThrow(); } } return len; } catch (Exception ex) { throw new RuntimeException(ex); }

Comentarios especiales en el modo de servidor

Acceso a campo comenzando a ganar después de cadenas de 32 caracteres de longitud en modo servidor en una máquina Java de 64 bits en mi máquina AMD 64. Eso no se vio hasta 512 caracteres de longitud en el modo cliente.

También vale la pena señalar, cuando estaba ejecutando JDK 8 (compilación de 32 bits) en el modo de servidor, el rendimiento general fue un 7% más lento para cadenas grandes y pequeñas. Esto fue con la versión compilada el 12 de diciembre de 2013 de JDK 8. Entonces, por ahora, parece que el modo de servidor de 32 bits es más lento que el modo de cliente de 32 bits.

Dicho eso ... parece que el único modo de servidor que vale la pena invocar es en una máquina de 64 bits. De lo contrario, realmente obstaculiza el rendimiento.

Para la compilación de 32 bits que se ejecuta en el -server mode en un AMD64, puedo decir esto:

  1. String.charAt (i) es el claro ganador en general. Aunque entre los tamaños de 8 a 512 caracteres hubo ganadores entre ''nuevo'' ''reutilización'' y ''campo''.
  2. String.charAt (i) es 45% más rápido en modo cliente
  3. El acceso de campo es dos veces más rápido para cadenas grandes en modo cliente.

También vale la pena decir que String.chars () (Stream y la versión paralela) son un fracaso. Mucho más lento que de otra manera. La API de Streams es una forma bastante lenta de realizar operaciones generales de cadenas.

Lista de deseos

Java String podría tener un predicado que acepte métodos optimizados como contains (predicate), forEach (consumer), forEachWithIndex (consumer). Por lo tanto, sin la necesidad de que el usuario conozca la longitud o la repetición de llamadas a los métodos String, esto podría ayudar a analizar las bibliotecas beep-beep beep speedup.

Sigue soñando :)

Happy Strings!

~ SH

La prueba utilizó los siguientes 9 métodos de prueba de la cadena para la presencia de espacios en blanco:

"charAt1" - COMPRUEBE EL CONTENIDO DE STRING DE MANERA USUAL:

int charAtMethod1(final String data) { final int len = data.length(); for (int i = 0; i < len; i++) { if (data.charAt(i) <= '' '') { doThrow(); } } return len; }

"charAt2" - IGUAL QUE ARRIBA, PERO USE String.length () EN LUGAR DE HACER UN LOCAL FINAL int EN LA LONGITUD

int charAtMethod2(final String data) { for (int i = 0; i < data.length(); i++) { if (data.charAt(i) <= '' '') { doThrow(); } } return data.length(); }

"stream" - UTILICE EL NUEVO IntStream de JAVA-8 String Y PASE UN PREDICADO PARA HACER LA COMPROBACIÓN

int streamMethod(final String data, final IntPredicate predicate) { if (data.chars().anyMatch(predicate)) { doThrow(); } return data.length(); }

"streamPara" - IGUAL QUE ARRIBA, PERO OH-LA-LA - ¡VAYA PARALELO!

// avoid this at all costs int streamParallelMethod(final String data, IntPredicate predicate) { if (data.chars().parallel().anyMatch(predicate)) { doThrow(); } return data.length(); }

"reutilizar" - RECARGA UN CHARÓN REUTILIZABLE [] CON LAS CUERDAS CONTENIDO

int reuseBuffMethod(final char[] reusable, final String data) { final int len = data.length(); data.getChars(0, len, reusable, 0); for (int i = 0; i < len; i++) { if (reusable[i] <= '' '') { doThrow(); } } return len; }

"nuevo1" - OBTENGA UNA NUEVA COPIA DEL CHAR [] DESDE EL STRING

int newMethod1(final String data) { final int len = data.length(); final char[] copy = data.toCharArray(); for (int i = 0; i < len; i++) { if (copy[i] <= '' '') { doThrow(); } } return len; }

"new2" - IGUAL QUE ARRIBA, PERO USAR "FOR-CADA"

int newMethod2(final String data) { for (final char c : data.toCharArray()) { if (c <= '' '') { doThrow(); } } return data.length(); }

"field1" - ¡FANCY! OBTENGA CAMPO PARA ACCEDER AL INTERIOR DE STRING []

int fieldMethod1(final Field field, final String data) { try { final char[] chars = (char[]) field.get(data); final int len = chars.length; for (int i = 0; i < len; i++) { if (chars[i] <= '' '') { doThrow(); } } return len; } catch (Exception ex) { throw new RuntimeException(ex); } }

"field2" - IGUAL QUE ARRIBA, PERO USAR "FOR-CADA"

int fieldMethod2(final Field field, final String data) { final char[] chars; try { chars = (char[]) field.get(data); } catch (Exception ex) { throw new RuntimeException(ex); } for (final char c : chars) { if (c <= '' '') { doThrow(); } } return chars.length; }

RESULTADOS -client PARA CLIENTE -client MODO DEL CLIENTE (pruebas adelante y atrás combinadas)

Nota: que el modo -client con Java 32 bit y el modo -server con Java 64 bit son los mismos que los de abajo en mi máquina AMD64.

Size WINNER charAt1 charAt2 stream streamPar reuse new1 new2 field1 field2 1 charAt 77.0 72.0 462.0 584.0 127.5 89.5 86.0 159.5 165.0 2 charAt 38.0 36.5 284.0 32712.5 57.5 48.3 50.3 89.0 91.5 4 charAt 19.5 18.5 458.6 3169.0 33.0 26.8 27.5 54.1 52.6 8 charAt 9.8 9.9 100.5 1370.9 17.3 14.4 15.0 26.9 26.4 16 charAt 6.1 6.5 73.4 857.0 8.4 8.2 8.3 13.6 13.5 32 charAt 3.9 3.7 54.8 428.9 5.0 4.9 4.7 7.0 7.2 64 charAt 2.7 2.6 48.2 232.9 3.0 3.2 3.3 3.9 4.0 128 charAt 2.1 1.9 43.7 138.8 2.1 2.6 2.6 2.4 2.6 256 charAt 1.9 1.6 42.4 90.6 1.7 2.1 2.1 1.7 1.8 512 field1 1.7 1.4 40.6 60.5 1.4 1.9 1.9 1.3 1.4 1,024 field1 1.6 1.4 40.0 45.6 1.2 1.9 2.1 1.0 1.2 2,048 field1 1.6 1.3 40.0 36.2 1.2 1.8 1.7 0.9 1.1 4,096 field1 1.6 1.3 39.7 32.6 1.2 1.8 1.7 0.9 1.0 8,192 field1 1.6 1.3 39.6 30.5 1.2 1.8 1.7 0.9 1.0 16,384 field1 1.6 1.3 39.8 28.4 1.2 1.8 1.7 0.8 1.0 32,768 field1 1.6 1.3 40.0 26.7 1.3 1.8 1.7 0.8 1.0 65,536 field1 1.6 1.3 39.8 26.3 1.3 1.8 1.7 0.8 1.0 131,072 field1 1.6 1.3 40.1 25.4 1.4 1.9 1.8 0.8 1.0 262,144 field1 1.6 1.3 39.6 25.2 1.5 1.9 1.9 0.8 1.0

RESULTADOS -server PARA SERVER -server MODE (pruebas adelante y atrás combinadas)

Nota: esta es la prueba para ejecutar Java 32 bit en modo servidor en un AMD64. El modo de servidor para Java 64 bit fue el mismo que Java 32 bit en modo cliente, excepto que el acceso de campo comenzó ganando después de 32 caracteres de tamaño.

Size WINNER charAt1 charAt2 stream streamPar reuse new1 new2 field1 field2 1 charAt 74.5 95.5 524.5 783.0 90.5 102.5 90.5 135.0 151.5 2 charAt 48.5 53.0 305.0 30851.3 59.3 57.5 52.0 88.5 91.8 4 charAt 28.8 32.1 132.8 2465.1 37.6 33.9 32.3 49.0 47.0 8 new2 18.0 18.6 63.4 1541.3 18.5 17.9 17.6 25.4 25.8 16 new2 14.0 14.7 129.4 1034.7 12.5 16.2 12.0 16.0 16.6 32 new2 7.8 9.1 19.3 431.5 8.1 7.0 6.7 7.9 8.7 64 reuse 6.1 7.5 11.7 204.7 3.5 3.9 4.3 4.2 4.1 128 reuse 6.8 6.8 9.0 101.0 2.6 3.0 3.0 2.6 2.7 256 field2 6.2 6.5 6.9 57.2 2.4 2.7 2.9 2.3 2.3 512 reuse 4.3 4.9 5.8 28.2 2.0 2.6 2.6 2.1 2.1 1,024 charAt 2.0 1.8 5.3 17.6 2.1 2.5 3.5 2.0 2.0 2,048 charAt 1.9 1.7 5.2 11.9 2.2 3.0 2.6 2.0 2.0 4,096 charAt 1.9 1.7 5.1 8.7 2.1 2.6 2.6 1.9 1.9 8,192 charAt 1.9 1.7 5.1 7.6 2.2 2.5 2.6 1.9 1.9 16,384 charAt 1.9 1.7 5.1 6.9 2.2 2.5 2.5 1.9 1.9 32,768 charAt 1.9 1.7 5.1 6.1 2.2 2.5 2.5 1.9 1.9 65,536 charAt 1.9 1.7 5.1 5.5 2.2 2.4 2.4 1.9 1.9 131,072 charAt 1.9 1.7 5.1 5.4 2.3 2.5 2.5 1.9 1.9 262,144 charAt 1.9 1.7 5.1 5.1 2.3 2.5 2.5 1.9 1.9

CÓDIGO DE PROGRAMA RUNNABLE COMPLETO

(para probar en Java 7 y versiones anteriores, elimine las dos pruebas de secuencias)

import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.function.IntPredicate; /** * @author Saint Hill <http://.com/users/1584255/saint-hill> */ public final class TestStrings { // we will not test strings longer than 512KM final int MAX_STRING_SIZE = 1024 * 256; // for each string size, we will do all the tests // this many times final int TRIES_PER_STRING_SIZE = 1000; public static void main(String[] args) throws Exception { new TestStrings().run(); } void run() throws Exception { // double the length of the data until it reaches MAX chars long // 0,1,2,4,8,16,32,64,128,256 ... final List<Integer> sizes = new ArrayList<>(); for (int n = 0; n <= MAX_STRING_SIZE; n = (n == 0 ? 1 : n * 2)) { sizes.add(n); } // CREATE RANDOM (FOR SHUFFLING ORDER OF TESTS) final Random random = new Random(); System.out.println("Rate in nanoseconds per character inspected."); System.out.printf("==== FORWARDS (tries per size: %s) ==== /n", TRIES_PER_STRING_SIZE); printHeadings(TRIES_PER_STRING_SIZE, random); for (int size : sizes) { reportResults(size, test(size, TRIES_PER_STRING_SIZE, random)); } // reverse order or string sizes Collections.reverse(sizes); System.out.println(""); System.out.println("Rate in nanoseconds per character inspected."); System.out.printf("==== BACKWARDS (tries per size: %s) ==== /n", TRIES_PER_STRING_SIZE); printHeadings(TRIES_PER_STRING_SIZE, random); for (int size : sizes) { reportResults(size, test(size, TRIES_PER_STRING_SIZE, random)); } } /// /// /// METHODS OF CHECKING THE CONTENTS /// OF A STRING. ALWAYS CHECKING FOR /// WHITESPACE (CHAR <='' '') /// /// // CHECK THE STRING CONTENTS int charAtMethod1(final String data) { final int len = data.length(); for (int i = 0; i < len; i++) { if (data.charAt(i) <= '' '') { doThrow(); } } return len; } // SAME AS ABOVE BUT USE String.length() // instead of making a new final local int int charAtMethod2(final String data) { for (int i = 0; i < data.length(); i++) { if (data.charAt(i) <= '' '') { doThrow(); } } return data.length(); } // USE new Java-8 String''s IntStream // pass it a PREDICATE to do the checking int streamMethod(final String data, final IntPredicate predicate) { if (data.chars().anyMatch(predicate)) { doThrow(); } return data.length(); } // OH LA LA - GO PARALLEL!!! int streamParallelMethod(final String data, IntPredicate predicate) { if (data.chars().parallel().anyMatch(predicate)) { doThrow(); } return data.length(); } // Re-fill a resuable char[] with the contents // of the String''s char[] int reuseBuffMethod(final char[] reusable, final String data) { final int len = data.length(); data.getChars(0, len, reusable, 0); for (int i = 0; i < len; i++) { if (reusable[i] <= '' '') { doThrow(); } } return len; } // Obtain a new copy of char[] from String int newMethod1(final String data) { final int len = data.length(); final char[] copy = data.toCharArray(); for (int i = 0; i < len; i++) { if (copy[i] <= '' '') { doThrow(); } } return len; } // Obtain a new copy of char[] from String // but use FOR-EACH int newMethod2(final String data) { for (final char c : data.toCharArray()) { if (c <= '' '') { doThrow(); } } return data.length(); } // FANCY! // OBTAIN FIELD FOR ACCESS TO THE STRING''S // INTERNAL CHAR[] int fieldMethod1(final Field field, final String data) { try { final char[] chars = (char[]) field.get(data); final int len = chars.length; for (int i = 0; i < len; i++) { if (chars[i] <= '' '') { doThrow(); } } return len; } catch (Exception ex) { throw new RuntimeException(ex); } } // same as above but use FOR-EACH int fieldMethod2(final Field field, final String data) { final char[] chars; try { chars = (char[]) field.get(data); } catch (Exception ex) { throw new RuntimeException(ex); } for (final char c : chars) { if (c <= '' '') { doThrow(); } } return chars.length; } /** * * Make a list of tests. We will shuffle a copy of this list repeatedly * while we repeat this test. * * @param data * @return */ List<Jobber> makeTests(String data) throws Exception { // make a list of tests final List<Jobber> tests = new ArrayList<Jobber>(); tests.add(new Jobber("charAt1") { int check() { return charAtMethod1(data); } }); tests.add(new Jobber("charAt2") { int check() { return charAtMethod2(data); } }); tests.add(new Jobber("stream") { final IntPredicate predicate = new IntPredicate() { public boolean test(int value) { return value <= '' ''; } }; int check() { return streamMethod(data, predicate); } }); tests.add(new Jobber("streamPar") { final IntPredicate predicate = new IntPredicate() { public boolean test(int value) { return value <= '' ''; } }; int check() { return streamParallelMethod(data, predicate); } }); // Reusable char[] method tests.add(new Jobber("reuse") { final char[] cbuff = new char[MAX_STRING_SIZE]; int check() { return reuseBuffMethod(cbuff, data); } }); // New char[] from String tests.add(new Jobber("new1") { int check() { return newMethod1(data); } }); // New char[] from String tests.add(new Jobber("new2") { int check() { return newMethod2(data); } }); // Use reflection for field access tests.add(new Jobber("field1") { final Field field; { field = String.class.getDeclaredField("value"); field.setAccessible(true); } int check() { return fieldMethod1(field, data); } }); // Use reflection for field access tests.add(new Jobber("field2") { final Field field; { field = String.class.getDeclaredField("value"); field.setAccessible(true); } int check() { return fieldMethod2(field, data); } }); return tests; } /** * We use this class to keep track of test results */ abstract class Jobber { final String name; long nanos; long chars; long runs; Jobber(String name) { this.name = name; } abstract int check(); final double nanosPerChar() { double charsPerRun = chars / runs; long nanosPerRun = nanos / runs; return charsPerRun == 0 ? nanosPerRun : nanosPerRun / charsPerRun; } final void run() { runs++; long time = System.nanoTime(); chars += check(); nanos += System.nanoTime() - time; } } // MAKE A TEST STRING OF RANDOM CHARACTERS A-Z private String makeTestString(int testSize, char start, char end) { Random r = new Random(); char[] data = new char[testSize]; for (int i = 0; i < data.length; i++) { data[i] = (char) (start + r.nextInt(end)); } return new String(data); } // WE DO THIS IF WE FIND AN ILLEGAL CHARACTER IN THE STRING public void doThrow() { throw new RuntimeException("Bzzzt -- Illegal Character!!"); } /** * 1. get random string of correct length 2. get tests (List<Jobber>) 3. * perform tests repeatedly, shuffling each time */ List<Jobber> test(int size, int tries, Random random) throws Exception { String data = makeTestString(size, ''A'', ''Z''); List<Jobber> tests = makeTests(data); List<Jobber> copy = new ArrayList<>(tests); while (tries-- > 0) { Collections.shuffle(copy, random); for (Jobber ti : copy) { ti.run(); } } // check to make sure all char counts the same long runs = tests.get(0).runs; long count = tests.get(0).chars; for (Jobber ti : tests) { if (ti.runs != runs && ti.chars != count) { throw new Exception("Char counts should match if all correct algorithms"); } } return tests; } private void printHeadings(final int TRIES_PER_STRING_SIZE, final Random random) throws Exception { System.out.print(" Size"); for (Jobber ti : test(0, TRIES_PER_STRING_SIZE, random)) { System.out.printf("%9s", ti.name); } System.out.println(""); } private void reportResults(int size, List<Jobber> tests) { System.out.printf("%6d", size); for (Jobber ti : tests) { System.out.printf("%,9.2f", ti.nanosPerChar()); } System.out.println(""); } }


Parece que niether es más rápido o más lento

public static void main(String arguments[]) { //Build a long string StringBuilder sb = new StringBuilder(); for(int j = 0; j < 10000; j++) { sb.append("a really, really long string"); } String str = sb.toString(); for (int testscount = 0; testscount < 10; testscount ++) { //Test 1 long start = System.currentTimeMillis(); for(int c = 0; c < 10000000; c++) { for (int i = 0, n = str.length(); i < n; i++) { char chr = str.charAt(i); doSomethingWithChar(chr);//To trick JIT optimistaion } } System.out.println("1: " + (System.currentTimeMillis() - start)); //Test 2 start = System.currentTimeMillis(); char[] chars = str.toCharArray(); for(int c = 0; c < 10000000; c++) { for (int i = 0, n = chars.length; i < n; i++) { char chr = chars[i]; doSomethingWithChar(chr);//To trick JIT optimistaion } } System.out.println("2: " + (System.currentTimeMillis() - start)); System.out.println(); } } public static void doSomethingWithChar(char chr) { int newInt = chr << 2; }

Para cuerdas largas elegiré la primera. ¿Por qué copiar alrededor de cadenas largas? Documentaciones dice:

public char [] toCharArray () Convierte esta cadena en una nueva matriz de caracteres.

Devuelve: una matriz de caracteres recién asignada cuya longitud es la longitud de esta cadena y cuyos contenidos se inicializan para contener la secuencia de caracteres representada por esta cadena.

// Editar 1

Cambié la prueba para engañar la optimización JIT.

// Edit 2

Repita la prueba 10 veces para permitir que JVM se caliente.

// Edit 3

Conclusiones

En primer lugar str.toCharArray(); copia toda la cadena en la memoria. Puede consumir memoria para cadenas largas. Método String.charAt( ) busca char en la matriz char dentro del índice de comprobación de clase String anterior. Parece que el primer método de Cadenas lo suficientemente corto (es decir, el método de chatAt ) es un poco más lento debido a esta comprobación de índice. Pero si el String es lo suficientemente largo, copiar todo el conjunto de caracteres se vuelve más lento, y el primer método es más rápido. Cuanto más larga sea la cadena, más lento toCharArray funcionamiento de toCharArray . Intenta cambiar el límite en for(int j = 0; j < 10000; j++) loop para verlo. Si permitimos que el código de calentamiento de JVM se ejecute más rápido, las proporciones son las mismas.

Después de todo, solo es una micro-optimización.


String.toCharArray() crea una nueva matriz de caracteres, significa la asignación de memoria de la longitud de la cadena, luego copia la matriz de caracteres original de la cadena mediante System.arraycopy() y luego devuelve esta copia a la persona que llama. String.charAt () devuelve el carácter en la posición i de la copia original, por eso String.charAt() será más rápido que String.toCharArray() . Aunque, String.toCharArray() devuelve copy y no char de la matriz String original, donde String.charAt() devuelve el carácter de la matriz char original. El siguiente código devuelve valor en el índice especificado de esta cadena.

public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index]; }

El siguiente código devuelve una matriz de caracteres recién asignada cuya longitud es la longitud de esta cadena

public char[] toCharArray() { // Cannot use Arrays.copyOf because of class initialization order issues char result[] = new char[value.length]; System.arraycopy(value, 0, result, 0, value.length); return result; }


Solo por curiosidad y para comparar con la respuesta de Saint Hill.

Si necesita procesar datos pesados, no debe usar JVM en modo cliente. El modo cliente no está hecho para optimizaciones.

Comparemos los resultados de los benchmarks @Saint Hill utilizando una JVM en modo Cliente y modo Servidor.

Core2Quad Q6600 G0 @ 2.4GHz JavaSE 1.7.0_40

Ver también: ¿ Diferencias reales entre "java -server" y "java -client"?

MODO CLIENTE:

len = 2: 111k charAt(i), 105k cbuff[i], 62k new[i], 17k field access. (chars/ms) len = 4: 285k charAt(i), 166k cbuff[i], 114k new[i], 43k field access. (chars/ms) len = 6: 315k charAt(i), 230k cbuff[i], 162k new[i], 69k field access. (chars/ms) len = 8: 333k charAt(i), 275k cbuff[i], 181k new[i], 85k field access. (chars/ms) len = 12: 342k charAt(i), 342k cbuff[i], 222k new[i], 117k field access. (chars/ms) len = 16: 363k charAt(i), 347k cbuff[i], 275k new[i], 152k field access. (chars/ms) len = 20: 363k charAt(i), 392k cbuff[i], 289k new[i], 180k field access. (chars/ms) len = 24: 375k charAt(i), 428k cbuff[i], 311k new[i], 205k field access. (chars/ms) len = 28: 378k charAt(i), 474k cbuff[i], 341k new[i], 233k field access. (chars/ms) len = 32: 376k charAt(i), 492k cbuff[i], 340k new[i], 251k field access. (chars/ms) len = 64: 374k charAt(i), 551k cbuff[i], 374k new[i], 367k field access. (chars/ms) len = 128: 385k charAt(i), 624k cbuff[i], 415k new[i], 509k field access. (chars/ms) len = 256: 390k charAt(i), 675k cbuff[i], 436k new[i], 619k field access. (chars/ms) len = 512: 394k charAt(i), 703k cbuff[i], 439k new[i], 695k field access. (chars/ms) len = 1024: 395k charAt(i), 718k cbuff[i], 462k new[i], 742k field access. (chars/ms) len = 2048: 396k charAt(i), 725k cbuff[i], 471k new[i], 767k field access. (chars/ms) len = 4096: 396k charAt(i), 727k cbuff[i], 459k new[i], 780k field access. (chars/ms) len = 8192: 397k charAt(i), 712k cbuff[i], 446k new[i], 772k field access. (chars/ms)

MODO SERVIDOR:

len = 2: 86k charAt(i), 41k cbuff[i], 46k new[i], 80k field access. (chars/ms) len = 4: 571k charAt(i), 250k cbuff[i], 97k new[i], 222k field access. (chars/ms) len = 6: 666k charAt(i), 333k cbuff[i], 125k new[i], 315k field access. (chars/ms) len = 8: 800k charAt(i), 400k cbuff[i], 181k new[i], 380k field access. (chars/ms) len = 12: 800k charAt(i), 521k cbuff[i], 260k new[i], 545k field access. (chars/ms) len = 16: 800k charAt(i), 592k cbuff[i], 296k new[i], 640k field access. (chars/ms) len = 20: 800k charAt(i), 666k cbuff[i], 408k new[i], 800k field access. (chars/ms) len = 24: 800k charAt(i), 705k cbuff[i], 452k new[i], 800k field access. (chars/ms) len = 28: 777k charAt(i), 736k cbuff[i], 368k new[i], 933k field access. (chars/ms) len = 32: 800k charAt(i), 780k cbuff[i], 571k new[i], 969k field access. (chars/ms) len = 64: 800k charAt(i), 901k cbuff[i], 800k new[i], 1306k field access. (chars/ms) len = 128: 1084k charAt(i), 888k cbuff[i], 633k new[i], 1620k field access. (chars/ms) len = 256: 1122k charAt(i), 966k cbuff[i], 729k new[i], 1790k field access. (chars/ms) len = 512: 1163k charAt(i), 1007k cbuff[i], 676k new[i], 1910k field access. (chars/ms) len = 1024: 1179k charAt(i), 1027k cbuff[i], 698k new[i], 1954k field access. (chars/ms) len = 2048: 1184k charAt(i), 1043k cbuff[i], 732k new[i], 2007k field access. (chars/ms) len = 4096: 1188k charAt(i), 1049k cbuff[i], 742k new[i], 2031k field access. (chars/ms) len = 8192: 1157k charAt(i), 1032k cbuff[i], 723k new[i], 2048k field access. (chars/ms)

CONCLUSIÓN:

Como puede ver, el modo del servidor es mucho más rápido.


Estoy bastante seguro de que el primero será más rápido.

En el segundo, agregaste otra operación para convertir la secuencia en char ... Perdiste un par de veces aquí ...