web-dev-qa-db-fra.com

String replaceAll () vs Matcher replaceAll () (Différences de performances)

C'est une question assez simple, mais cela vient d'une personne en C/C++ qui entre dans les subtilités de Java.

Je comprends que je peux lancer jUnit et quelques tests de performance pour obtenir une réponse. mais je me demande si c'est là-bas.

Existe-t-il des différences connues entre String.replaceAll () et Matcher.replaceAll () (sur un objet Matcher créé à partir d'un objet Regex.Pattern) en termes de performances?

En outre, quelles sont les différences ish de l'API de haut niveau entre les deux? (Immutabilité, traitement des valeurs NULL, traitement des chaînes vides, préparation du café, etc.)

42
Suvesh Pratapa

Selon la documentation de String.replaceAll , il est indiqué à propos de l’appel de la méthode:

Une invocation de cette méthode du forme str.replaceAll(regex, repl) donne exactement le même résultat que le expression

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

Par conséquent, on peut s'attendre à ce que la performance entre l'appel de String.replaceAll et la création explicite d'un Matcher et Pattern soit identique.

Modifier

Comme indiqué dans les commentaires, la différence de performances étant inexistante, il en irait de même pour un appel unique à replaceAll à partir de String ou Matcher. Toutefois, si vous devez effectuer plusieurs appels à replaceAll, vous vous attendez à ce que cela soit avantageux conservez un Pattern compilé, de sorte que la compilation relativement coûteuse de modèles d’expression régulière ne soit pas nécessairement effectuée à chaque fois.

71
coobird

Code source de String.replaceAll():

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

Le modèle doit d'abord être compilé - si vous l'exécutez plusieurs fois avec le même modèle sur des chaînes courtes, les performances seront bien meilleures si vous réutilisez un modèle compilé.

22
Michael Borgwardt

La principale différence est que si vous conservez le Pattern utilisé pour produire le Matcher, vous pouvez éviter de recompiler la regex chaque fois que vous l’utilisez. En passant par String, vous n’avez pas la possibilité de "mettre en cache" de la sorte.

Si vous avez une expression rationnelle différente à chaque fois, utiliser String de la classe replaceAll convient parfaitement. Si vous appliquez la même expression rationnelle à plusieurs chaînes, créez un Pattern et réutilisez-le.

9
erickson

Immutabilité/sécurité des threads: les patterns compilés sont immuables, les correspondants ne le sont pas. (voir Java Regex Thread Safe? )

Gestion des chaînes vides: replaceAll doit gérer les chaînes vides avec élégance (il ne correspondra pas à un modèle de chaîne d'entrée vide)

Faire du café, etc.: aux dernières nouvelles, ni String, ni Pattern, ni Matcher n’avaient de fonction API pour cela.

edit: en ce qui concerne la gestion des valeurs NULL, la documentation de String et Pattern ne le dit pas explicitement, mais je soupçonne qu’ils lèveraient une exception NullPointerException car ils s’attendent à une chaîne.

6
Jason S

La différence est que String.replaceAll () compile l'expression régulière à chaque appel. Il n'y a pas d'équivalent pour la méthode statique Regex.Replace () de .NET, qui met automatiquement en cache la regex compilée. En règle générale, replaceAll () est quelque chose que vous ne faites qu'une fois, mais si vous voulez l'appeler de manière répétée avec la même expression régulière, en particulier dans une boucle, vous devez créer un objet Pattern et utiliser la méthode Matcher.

Vous pouvez également créer Matcher à l'avance et utiliser sa méthode reset () pour le recibler à chaque utilisation:

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

Bien entendu, l’avantage de la réutilisation du Matcher sur les performances n’est comparable à celui de la réutilisation du motif.

4
Alan Moore

L'implémentation de String.replaceAll vous dit tout ce que vous devez savoir:

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

(Et les docs disent la même chose.)

Bien que je n'ai pas vérifié la mise en cache, je m'attendrais certainement à ce que la compilation d'un modèle une fois et le maintien d'une référence statique à ce dernier soient plus efficaces que d'appeler Pattern.compile avec le même modèle à chaque fois. S'il y a un cache, ce sera une petite économie d'efficacité - s'il n'y en avait pas, ce pourrait être un grand.

4
Jon Skeet

Les autres réponses couvrent suffisamment la partie performance du PO, mais une autre différence entre Matcher::replaceAll et String::replaceAll permet également de compiler votre propre Pattern. Lorsque vous compilez vous-même une Pattern, il existe des options telles que des indicateurs pour modifier la façon dont la regex est appliquée. Par exemple:

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

La Matcher appliquera tous les indicateurs que vous avez définis lorsque vous appelez Matcher::replaceAll.

Vous pouvez également définir d’autres indicateurs. Je voulais surtout souligner que les API Pattern et Matcher ont beaucoup d'options, et c'est la raison principale d'aller au-delà du simple String::replaceAll

0
Indigenuity