2009-06-18 15 views
12

El hecho de que el método replace devuelva un objeto de cadena en lugar de reemplazar el contenido de una cadena dada es un poco obtuso (pero comprensible cuando se sabe que las cadenas son inmutables en Java). Estoy teniendo un gran golpe de rendimiento mediante el uso de un reemplazo profundamente anidado en algún código. ¿Hay algo con lo que pueda reemplazarlo que lo haga más rápido?Alternativas más rápidas para reemplazar el método en Java String?

+5

heh heh replace replace – ojblass

+1

¿Usted usa strings? ¿estas loco? usa array de byte! – IAdapter

Respuesta

19

Esto es para lo que StringBuilder se destina. Si va a hacer mucha manipulación, hágalo en un StringBuilder, luego conviértalo en un String siempre que lo necesite.

StringBuilder se describe así:

"Una secuencia mutable de caracteres Esta clase proporciona una API compatible con StringBuffer, pero sin ninguna garantía de sincronización".

Tiene replace (y append, insert, delete, et al) y se puede utilizar toString de transformarse en un verdadero String.

+0

Recuerde también utilizar StringBuilder si no requiere seguridad de subprocesos, generalmente es más rápido y funciona de la misma manera. –

+0

Además, StringBuilder.replace funciona de manera bastante diferente a String.replace, por lo que no puede usarlo como reemplazo de reemplazo. –

0

Toda la manipulación de cadenas en general es muy lenta. Considere usar StringBuffer, no es exactamente como la clase String, pero tiene mucho en común y también es mutable.

+1

En general, si no necesita que su búfer sea seguro para subprocesos (es decir, no tiene varios subprocesos que manipulen el mismo búfer a la vez), debe usar StringBuilder en lugar de StringBuffer. – Avi

+2

De la documentación de StringBuffer: La clase StringBuilder generalmente se debe utilizar con preferencia a esta, ya que admite todas las mismas operaciones pero es más rápida, ya que no realiza ninguna sincronización. – tgamblin

+0

Solía ​​trabajar mucho con el entorno de subprocesos múltiples, así que StringBuffer vino a mi mente de forma natural. –

7

Las publicaciones anteriores son correctas, StringBuilder/StringBuffer son una solución.

Pero, también debe preguntarse si es una buena idea reemplazar las cadenas grandes en la memoria.

A menudo tengo manipulaciones de cadena que se implementan como una secuencia, por lo que en lugar de reemplazarlo en la cadena y luego enviarlo a un OutputStream, lo hago en el momento en que envío el String a la salida de salida. Eso funciona mucho más rápido que cualquier reemplazo.

Esto funciona mucho más rápido si desea que este reemplazo implemente un mecanismo de plantilla. La transmisión siempre es más rápida ya que consume menos memoria y si los clientes son lentos, solo necesita generar a un ritmo lento, por lo que escalará mucho mejor.

+2

¿Puedes dar un ejemplo? –

1

Si tiene que reemplazar varias cadenas (como secuencias de escape XML), especialmente cuando las sustituciones son de diferente longitud que el patrón, el algoritmo tipo FSM lexer parece ser más eficiente, similar a la sugerencia de procesamiento en forma de flujo, donde la salida se construye incrementalmente.

Quizás un objeto Matcher se podría usar para hacerlo de manera eficiente.

1

Simplemente obtenga el char[] del String y repítalo. Use un StringBuilder temporal.

Busque el patrón que desea reemplazar mientras itera si no encuentra el patrón, escriba las cosas escaneadas en StringBuilder, escriba el texto de reemplazo en StringBuilder.

2

Agregando a la respuesta @paxdiablo, aquí hay una implementación de muestra de un replaceAll usando StringBuffers que es ~ 3.7 veces más rápido que String.replaceAll():

Código:

public static String replaceAll(final String str, final String searchChars, String replaceChars) 
{ 
    if ("".equals(str) || "".equals(searchChars) || searchChars.equals(replaceChars)) 
    { 
    return str; 
    } 
    if (replaceChars == null) 
    { 
    replaceChars = ""; 
    } 
    final int strLength = str.length(); 
    final int searchCharsLength = searchChars.length(); 
    StringBuilder buf = new StringBuilder(str); 
    boolean modified = false; 
    for (int i = 0; i < strLength; i++) 
    { 
    int start = buf.indexOf(searchChars, i); 

    if (start == -1) 
    { 
     if (i == 0) 
     { 
     return str; 
     } 
     return buf.toString(); 
    } 
    buf = buf.replace(start, start + searchCharsLength, replaceChars); 
    modified = true; 

    } 
    if (!modified) 
    { 
    return str; 
    } 
    else 
    { 
    return buf.toString(); 
    } 
} 

caso de prueba - La salida es la siguiente (Delta1 = 1917009502; Delta2 = 7241000026):

@Test 
public void testReplaceAll() 
{ 
    String origStr = "1234567890-1234567890-"; 

    String replacement1 = StringReplacer.replaceAll(origStr, "0", "a"); 
    String expectedRep1 = "123456789a-123456789a-"; 

    String replacement2 = StringReplacer.replaceAll(origStr, "0", "ab"); 
    String expectedRep2 = "123456789ab-123456789ab-"; 

    String replacement3 = StringReplacer.replaceAll(origStr, "0", ""); 
    String expectedRep3 = "123456789-123456789-"; 


    String replacement4 = StringReplacer.replaceAll(origStr, "012", "a"); 
    String expectedRep4 = "1234567890-1234567890-"; 

    String replacement5 = StringReplacer.replaceAll(origStr, "123", "ab"); 
    String expectedRep5 = "ab4567890-ab4567890-"; 

    String replacement6 = StringReplacer.replaceAll(origStr, "123", "abc"); 
    String expectedRep6 = "abc4567890-abc4567890-"; 

    String replacement7 = StringReplacer.replaceAll(origStr, "123", "abcdd"); 
    String expectedRep7 = "abcdd4567890-abcdd4567890-"; 

    String replacement8 = StringReplacer.replaceAll(origStr, "123", ""); 
    String expectedRep8 = "4567890-4567890-"; 

    String replacement9 = StringReplacer.replaceAll(origStr, "123", ""); 
    String expectedRep9 = "4567890-4567890-"; 

    assertEquals(replacement1, expectedRep1); 
    assertEquals(replacement2, expectedRep2); 
    assertEquals(replacement3, expectedRep3); 
    assertEquals(replacement4, expectedRep4); 
    assertEquals(replacement5, expectedRep5); 
    assertEquals(replacement6, expectedRep6); 
    assertEquals(replacement7, expectedRep7); 
    assertEquals(replacement8, expectedRep8); 
    assertEquals(replacement9, expectedRep9); 

    long start1 = System.nanoTime(); 
    for (long i = 0; i < 10000000L; i++) 
    { 
    String rep = StringReplacer.replaceAll(origStr, "123", "abcdd"); 
    } 
    long delta1 = System.nanoTime() -start1; 

    long start2= System.nanoTime(); 

    for (long i = 0; i < 10000000L; i++) 
    { 
    String rep = origStr.replaceAll("123", "abcdd"); 
    } 

    long delta2 = System.nanoTime() -start1; 

    assertTrue(delta1 < delta2); 

    System.out.printf("Delta1 = %d; Delta2 =%d", delta1, delta2); 


} 
0

Cuando usted está reemplazando caracteres individuales , considere iterar sobre su matriz de caracteres, pero reemplace caracteres utilizando un HashMap<Character, Character>() (previamente creado).

Utilizo esta estrategia para convertir una cadena de exponente entero por caracteres de superíndice Unicode.

Es aproximadamente el doble de rápido en comparación con String.replace(char, char). Tenga en cuenta que el tiempo asociado a la creación del mapa hash no está incluido en esta comparación.

3

El siguiente código es aprox. 30 veces más rápido si no hay coincidencia y 5 veces más rápido si hay una coincidencia.

static String fastReplace(String str, String target, String replacement) { 
    int targetLength = target.length(); 
    if(targetLength == 0) { 
     return str; 
    } 
    int idx2 = str.indexOf(target); 
    if(idx2 < 0) { 
     return str; 
    } 
    StringBuilder buffer = new StringBuilder(targetLength > replacement.length() ? str.length() : str.length() * 2); 
    int idx1 = 0; 
    do { 
     buffer.append(str, idx1, idx2); 
     buffer.append(replacement); 
     idx1 = idx2 + targetLength; 
     idx2 = str.indexOf(target, idx1); 
    } while(idx2 > 0); 
    buffer.append(str, idx1, str.length()); 
    return buffer.toString(); 
} 
Cuestiones relacionadas