J'ai besoin de remplacer de nombreuses sous-chaînes différentes dans une chaîne de la manière la plus efficace. y a-t-il un autre moyen, autre que celui de la force brute, de remplacer chaque champ en utilisant string.replace?
97
Si la chaîne sur laquelle vous travaillez est très longue ou si vous utilisez de nombreuses chaînes, il peut être intéressant d'utiliser un java.util.regex.Matcher (cela demande du temps à l'avance pour la compilation, donc ce ne sera pas efficace si votre entrée est très petite ou votre modèle de recherche change fréquemment).
Vous trouverez ci-dessous un exemple complet, basé sur une liste de jetons tirés d'une carte. (Utilise StringUtils d'Apache Commons Lang).
Map<String,String> tokens = new HashMap<String,String>();
tokens.put("cat", "Garfield");
tokens.put("beverage", "coffee");
String template = "%cat% really needs some %beverage%.";
// Create pattern of the format "%(cat|beverage)%"
String patternString = "%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(template);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
}
matcher.appendTail(sb);
System.out.println(sb.toString());
Une fois l'expression régulière compilée, l'analyse de la chaîne d'entrée est généralement très rapide (bien que si votre expression régulière est complexe ou implique un retour en arrière, vous devrez toujours effectuer un benchmark pour le confirmer!)
"%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Algorithme
L'un des moyens les plus efficaces de remplacer les chaînes correspondantes (sans expressions régulières) consiste à utiliser l' algorithme Aho-Corasick avec un Trie performant (prononcé "try"), un algorithme de hachage rapide et une implémentation efficace des collections .
Code simple
Une solution simple tire parti d'Apache
StringUtils.replaceEach
comme suit:Cela ralentit sur les gros textes.
Code rapide
L'implémentation par Bor de l'algorithme Aho-Corasick introduit un peu plus de complexité qui devient un détail d'implémentation en utilisant une façade avec la même signature de méthode:
Benchmarks
Pour les benchmarks, le tampon a été créé en utilisant randomNumeric comme suit:
Où
MATCHES_DIVISOR
dicte le nombre de variables à injecter:Le code de référence lui-même ( JMH semblait exagéré):
1 000 000: 1 000
Un micro-benchmark simple avec 1 000 000 de caractères et 1 000 chaînes placées au hasard à remplacer.
Pas de compétition.
10 000: 1 000
Utilisation de 10 000 caractères et 1 000 chaînes correspondantes pour remplacer:
Le fossé se referme.
1 000: 10
Utilisation de 1000 caractères et 10 chaînes correspondantes pour remplacer:
Pour les chaînes courtes, la surcharge de configuration d'Aho-Corasick éclipse l'approche de la force brute de
StringUtils.replaceEach
.Une approche hybride basée sur la longueur du texte est possible, pour tirer le meilleur parti des deux implémentations.
Implémentations
Envisagez de comparer d'autres implémentations pour du texte de plus de 1 Mo, notamment:
Papiers
Articles et informations relatifs à l'algorithme:
la source
Cela a fonctionné pour moi:
Exemple:
Sortie: pomme-banane-frui-
la source
Si vous allez changer une chaîne plusieurs fois, il est généralement plus efficace d'utiliser un StringBuilder (mais mesurez vos performances pour le savoir) :
Chaque fois que vous effectuez un remplacement sur une chaîne, un nouvel objet String est créé, car les chaînes sont immuables. StringBuilder est modifiable, c'est-à-dire qu'il peut être modifié autant que vous le souhaitez.
la source
StringBuilder
effectuera le remplacement plus efficacement, puisque son tampon de tableau de caractères peut être spécifié à une longueur requise.StringBuilder
est conçu pour plus que l'ajout!Bien sûr, la vraie question est de savoir si c'est une optimisation trop loin? La JVM est très efficace pour gérer la création de plusieurs objets et le ramasse-miettes qui s'ensuit, et comme toutes les questions d'optimisation, ma première question est de savoir si vous avez mesuré cela et déterminé que c'est un problème.
la source
Que diriez-vous d'utiliser la méthode replaceAll () ?
la source
str.replaceAll(search1, replace1).replaceAll(search2, replace2).replaceAll(search3, replace3).replaceAll(search4, replace4)
Rythm un moteur de template java maintenant publié avec une nouvelle fonctionnalité appelée mode d'interpolation String qui vous permet de faire quelque chose comme:
Le cas ci-dessus montre que vous pouvez passer l'argument au modèle par position. Rythm vous permet également de passer des arguments par nom:
Remarque Rythm est TRÈS RAPIDE, environ 2 à 3 fois plus rapide que String.format et vélocité, car il compile le modèle en code octet java, les performances d'exécution sont très proches de la concatentation avec StringBuilder.
Liens:
la source
"%cat% really needs some %beverage%.";
n'est-il pas%
un format prédéfini? Votre premier point est encore plus drôle, JDK fournit un tas de "vieilles capacités", certaines commencent à partir des années 90, pourquoi les gens se donnent-ils la peine de les utiliser? Vos commentaires et votre vote négatif n'ont aucun sensCe qui suit est basé sur la réponse de Todd Owen . Cette solution pose le problème que si les remplacements contiennent des caractères qui ont une signification particulière dans les expressions régulières, vous pouvez obtenir des résultats inattendus. Je voulais également pouvoir éventuellement faire une recherche insensible à la casse. Voici ce que j'ai trouvé:
Voici mes cas de test unitaires:
la source
la source
Vérifie ça:
Par exemple:
la source
Résumé: Implémentation de classe unique de la réponse de Dave, pour choisir automatiquement le plus efficace des deux algorithmes.
Il s'agit d'une implémentation complète et unique basée sur l'excellente réponse ci-dessus de Dave Jarvis . La classe choisit automatiquement entre les deux différents algorithmes fournis, pour une efficacité maximale. (Cette réponse s'adresse aux personnes qui souhaitent simplement copier et coller rapidement.)
Classe ReplaceStrings:
Dépendances Maven nécessaires:
(Ajoutez-les à votre fichier pom si nécessaire.)
la source