2011-03-29 20 views
25

El manejo de cadenas en Java es algo que trato de aprender a hacer bien. Actualmente quiero tomar una cadena y reemplazar cualquier personaje que encuentre.¿Cuál es una forma eficiente de reemplazar muchos caracteres en una cadena?

Aquí está mi función actual ineficiente (y algo tonta IMO). Fue escrito para solo trabajar.

public String convertWord(String word) 
{ 
    return word.toLowerCase().replace('á', 'a') 
          .replace('é', 'e') 
          .replace('í', 'i') 
          .replace('ú', 'u') 
          .replace('ý', 'y') 
          .replace('ð', 'd') 
          .replace('ó', 'o') 
          .replace('ö', 'o') 
          .replaceAll("[-]", "") 
          .replaceAll("[.]", "") 
          .replaceAll("[/]", "") 
          .replaceAll("[æ]", "ae") 
          .replaceAll("[þ]", "th"); 
} 

me corrieron 1.000.000 carreras de la misma y se tomaron 8182ms. Entonces, ¿cómo debo proceder para cambiar esta función y hacerla más eficiente?

Solución encontrado:

la conversión de la función a esta

public String convertWord(String word) 
{ 
    StringBuilder sb = new StringBuilder(); 

    char[] charArr = word.toLowerCase().toCharArray(); 

    for(int i = 0; i < charArr.length; i++) 
    { 
     // Single character case 
     if(charArr[i] == 'á') 
     { 
      sb.append('a'); 
     } 
     // Char to two characters 
     else if(charArr[i] == 'þ') 
     { 
      sb.append("th"); 
     } 
     // Remove 
     else if(charArr[i] == '-') 
     { 
     } 
     // Base case 
     else 
     { 
      sb.append(word.charAt(i)); 
     } 
    } 

    return sb.toString(); 
} 

La ejecución de esta función de 1.000.000 veces tarda 518ms. Entonces creo que eso es lo suficientemente eficiente. Gracias por la ayuda chicos :)

+3

* * Algunos del trabajo está aquí: http://stackoverflow.com/questions/1008802/converting-symbols-accent-letters-to-english-alphabet. No sé sobre 'æ' y' þ'. – Kobi

Respuesta

19

Se puede crear una tabla de String [] que es Character.MAX_VALUE de longitud. (Incluyendo la asignación a minúsculas)

A medida que las sustituciones se hicieron más complejas, el tiempo para llevarlas a cabo seguiría siendo el mismo.

private static final String[] REPLACEMENT = new String[Character.MAX_VALUE+1]; 
static { 
    for(int i=Character.MIN_VALUE;i<=Character.MAX_VALUE;i++) 
     REPLACEMENT[i] = Character.toString(Character.toLowerCase((char) i)); 
    // substitute 
    REPLACEMENT['á'] = "a"; 
    // remove 
    REPLACEMENT['-'] = ""; 
    // expand 
    REPLACEMENT['æ'] = "ae"; 
} 

public String convertWord(String word) { 
    StringBuilder sb = new StringBuilder(word.length()); 
    for(int i=0;i<word.length();i++) 
     sb.append(REPLACEMENT[word.charAt(i)]); 
    return sb.toString(); 
} 
+0

Esta es la solución que me ayudó a crear mi código actual. Así que estoy aceptando esto. Pero mikera ayudó mucho también. –

+1

Parecía un poco loco al principio, pero resulta que la matriz es de solo 64 KB, lo que no está nada mal. – Kobi

+0

@Kobi, esta búsqueda es muy rápida y no requiere ningún objeto. La convertWord() solo crea un objeto temporal (StringBuilder) –

8

Mi sugerencia sería:

  • convertir la cadena en un char [] array
  • Ejecutar a través de la matriz, las pruebas de cada caracteres de uno en uno (por ejemplo, con un interruptor declaración) y reemplazarlo si es necesario
  • convertir el [] matriz de caracteres de nuevo a una cadena

Creo que este es probablemente el rendimiento más rápido obtendrá en Java puro.

EDIT: Me doy cuenta de que estás haciendo algunos cambios que cambian la longitud de la cadena. En este caso, se aplica el mismo principio, sin embargo, debe mantener dos matrices e incrementar un índice de origen y un índice de destino por separado. Es posible que también necesite cambiar el tamaño de la matriz de destino si se queda sin espacio objetivo (es decir, reasignar una matriz más grande y crear una matriz de destino existente en ella)

+1

Esto podría funcionar. –

+1

Básicamente, debe iterar sobre los caracteres y usar un 'StringBuilder'. – Kobi

+1

yo haría lo mismo, con una pequeña variación, si no les importa usar un poco más de espacio: Me gustaría utilizar un 'Mapa ', donde las claves son el conjunto de caracteres que desea reemplazar, y los valores son los reemplazos correspondientes. Esto evita la instrucción 'switch'. – MarcoS

0

Dudo que pueda acelerar el 'reemplazo de caracteres' en absoluto De Verdad. En cuanto al reemplazo de expresiones regulares, puede compilar las expresiones regulares de antemano

0

Use la función String.replaceAll. Buen artículo similar con lo que quiere: link

0

Cada vez que tenemos problemas como este, utilizamos expresiones regulares, ya que son la manera más rápida de lidiar con lo que está tratando de hacer.

¿Ya ha probado las expresiones regulares?

0

Lo que veo que es ineficiente es que vas a verificar nuevamente los caracteres que ya han sido reemplazados, lo cual es inútil.

que tendría la charArray de la instancia String, iterar sobre ella, y para cada correo no deseado carácter de una serie de if-else así:

char[] array = word.toCharArray(); 
for(int i=0; i<array.length; ++i){ 
    char currentChar = array[i]; 
    if(currentChar.equals('é')) 
     array[i] = 'e'; 
    else if(currentChar.equals('ö')) 
     array[i] = 'o'; 
    else if(//... 
} 
2

Mi primera opción sería utilizar un StringBuilder porque necesita eliminar algunos caracteres de la cadena.

La segunda opción sería repetir el lanzamiento de la matriz de caracteres y agregar el carácter tratado a otra serie del tamaño inicial de la cadena.Entonces necesitaría copiar la matriz para recortar las posibles posiciones no utilizadas.

Después de eso, me gustaría hacer algunas pruebas de rendimiento para ver bruja uno es mejor.

5

Mi aplicación se basa en la tabla de búsqueda.

public static String convertWord(String str) { 
    char[] words = str.toCharArray(); 
    char[] find = {'á','é','ú','ý','ð','ó','ö','æ','þ','-','.', 
      '/'}; 
    String[] replace = {"a","e","u","y","d","o","o","ae","th"}; 
    StringBuilder out = new StringBuilder(str.length()); 
    for (int i = 0; i < words.length; i++) { 
     boolean matchFailed = true; 
     for(int w = 0; w < find.length; w++) { 
      if(words[i] == find[w]) { 
       if(w < replace.length) { 
        out.append(replace[w]); 
       } 
       matchFailed = false; 
       break; 
      } 
     } 
     if(matchFailed) out.append(words[i]); 
    } 
    return out.toString(); 
} 
Cuestiones relacionadas