2009-09-23 16 views
37

Pregunta bastante simple, pero esta proviene de una persona C/C++ que se adentra en las complejidades de Java.Cadena replaceAll() vs. Matcher replaceAll() (Diferencias de rendimiento)

Entiendo que puedo iniciar jUnit y algunas pruebas de rendimiento propias para obtener una respuesta; pero me pregunto si esto está ahí fuera.

¿Existen diferencias conocidas entre String.replaceAll() y Matcher.replaceAll() (En un objeto de Matcher creado a partir de un Regex.Pattern) en términos de rendimiento?

Además, ¿cuáles son las diferencias API de alto nivel entre ambas? (Inmutabilidad, Manejo de NULL, Manejo de cadenas vacías, haciendo café, etc.)

Respuesta

62

De acuerdo con la documentación para String.replaceAll, que tiene el siguiente a decir sobre el método:

Una invocación de este método de la forma str.replaceAll(regex, repl) produce exactamente el mismo resultado que la expresión

Pattern.compile(regex).matcher(str).replaceAll(repl) 

Por lo tanto, se puede esperar el rendimiento entre la invocación del String.replaceAll, y crear explícitamente un Matcher y Pattern debe ser el mismo.

Editar

Como se ha señalado en los comentarios, la diferencia de rendimiento es inexistente sería cierto para una sola llamada a replaceAll de String o Matcher, sin embargo, si uno necesita para realizar múltiples llamadas a replaceAll, se esperaría que fuera beneficioso conservar un compilado Pattern, por lo que la compilación del patrón de expresiones regulares relativamente caro no tiene que realizarse siempre.

+8

excepto, como se menciona a continuación, la penalización de rendimiento de la compilación del patrón. si está utilizando una expresión regular constante, compílela y péguela en una constante estática. – james

+2

Su comentario "Por lo tanto" al final solo aplica para 1 llamada, en cuyo caso las métricas de rendimiento realmente no son relevantes. Si hay llamadas repetidas para replaceAll con la misma expresión regular, String.replaceAll es más lento que el almacenamiento en caché de un patrón compilado. –

+0

Gracias por los buenos puntos, he editado mi respuesta. – coobird

3

La implementación de String.replaceAll contiene todo lo que necesita saber: (. Y los documentos dicen lo mismo)

return Pattern.compile(regex).matcher(this).replaceAll(replacement); 

Aunque no he comprobado el almacenamiento en caché, ciertamente esperaría que compilar un patrón una vez y mantener una referencia estática sea más eficiente que llamar a Pattern.compile con el mismo patrón cada vez. Si hay un caché, será un ahorro de eficiencia pequeño; si no lo hay, podría ser uno grande.

9

La diferencia principal es que si se mantiene en el Pattern utilizado para producir el Matcher, puede evitar volver a compilar la expresión regular cada vez que lo utilice. Pasando por String, no tienes la capacidad de "caché" de esta manera.

Si tiene una expresión regular diferente cada vez, usar el String clase replaceAll está bien. Si está aplicando la misma expresión regular a muchas cadenas, cree una Pattern y vuelva a utilizarla.

+1

Parcheando su respuesta para repetir lo que ya he dicho es cojo. – erickson

+0

Si eso fue dirigido a mí por alguna razón, sospecho que ya estaba editando para cuando publicaste tu respuesta ... –

+0

En realidad, estaba dirigida a coobird. – erickson

20

El código fuente de String.replaceAll():

public String replaceAll(String regex, String replacement) { 
    return Pattern.compile(regex).matcher(this).replaceAll(replacement); 
} 

Tiene que compilar el primer patrón - si se va a ejecutar muchas veces con el mismo patrón en cadenas cortas, el rendimiento será mucho mejor si vuelve a utilizar un patrón compilado.

6

Inmutabilidad/seguridad de hilo: compilado Los patrones son inmutables, los Matchers no lo son. (Ver Is Java Regex Thread Safe?)

Manejo de cadenas vacías: replaceAll debe manejar con gracia cadenas vacías (que no coincidirá con el patrón de encordado de entrada vacío)

Haciendo café, etc .: lo último que oí, ni cuerdas, ni patrón, ni Matcher tenía alguna característica API para eso.

editar: en cuanto a la manipulación de valores NULL, la documentación para Cadena y Patrón no lo dice explícitamente, pero sospecho que arrojarían una NullPointerException ya que esperan una Cadena.

3

La diferencia es que String.replaceAll() compila la expresión regular cada vez que se invoca. No hay un equivalente para el método Regex.Replace() estático de .NET, que almacena automáticamente en caché la expresión regular compilada. Por lo general, replaceAll() es algo que solo hace una vez, pero si va a llamarlo repetidamente con la misma expresión regular, especialmente en un bucle, debe crear un objeto Pattern y usar el método Matcher.

Puede crear el Matcher antes de tiempo, también, y utilizar su método de reset() para redirigir que para cada uso:

Matcher m = Pattern.compile(regex).matcher(""); 
for (String s : targets) 
{ 
    System.out.println(m.reset(s).replaceAll(repl)); 
} 

La ventaja de rendimiento de reutilizar el Matcher, por supuesto, no está tan genial como el de reutilizar el Patrón.

0

Las otras respuestas cubren suficientemente la parte de rendimiento del OP, pero otra diferencia entre Matcher::replaceAll y String::replaceAll es también una razón para compilar su propio Pattern. Cuando compila usted mismo un Pattern, hay opciones como indicadores para modificar cómo se aplica la expresión regular. Por ejemplo:

Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE); 

El Matcher se aplicarán todas las banderas estableció cuando se llama a Matcher::replaceAll.

También hay otros indicadores que puede establecer. Sobre todo, quería señalar que la API Pattern y Matcher tiene muchas opciones, y esa es la razón principal para ir más allá del simple String::replaceAll

Cuestiones relacionadas