2009-06-18 8 views
12

Le fait que la méthode replace renvoie un objet chaîne plutôt que de remplacer le contenu d'une chaîne donnée est un peu obtus (mais compréhensible quand on sait que les chaînes sont immuables en Java). Je prends un coup de performance majeur en utilisant un remplacement profondément imbriqué dans du code. Y a-t-il quelque chose que je puisse remplacer avec qui le rendrait plus rapide?Alternatives plus rapides pour remplacer la méthode dans une chaîne Java?

+5

heh heh remplacer remplacer – ojblass

+1

Vous utilisez des chaînes? êtes-vous fou? utilisez un tableau d'octets! – IAdapter

Répondre

19

C'est ce à quoi StringBuilder est destiné. Si vous allez faire beaucoup de manipulation, faites-le sur un StringBuilder, puis transformez cela en String quand vous en avez besoin.

StringBuilder est décrit ainsi:

"Une séquence de caractères mutable Cette classe fournit une API compatible avec StringBuffer, mais sans garantie de synchronisation".

Il a replace (et append, insert, delete, et al) et vous pouvez utiliser toString pour se transformer en un véritable String.

+0

Rappelez-vous également d'utiliser StringBuilder si vous n'avez pas besoin de sécurité de thread, c'est généralement plus rapide et fonctionne de la même manière. –

+0

En outre, StringBuilder.replace fonctionne très différemment de String.replace, vous ne pouvez donc pas l'utiliser comme remplacement direct! –

0

Toutes les manipulations de chaînes sont en général très lentes. Pensez à utiliser StringBuffer, ce n'est pas exactement comme la classe String, mais il a beaucoup en commun et il est également modifiable.

+1

En général, si vous n'avez pas besoin que votre buffer soit thread-safe (c'est-à-dire que vous n'avez pas plusieurs threads manipulant le même buffer en même temps), vous devez utiliser StringBuilder au lieu de StringBuffer. – Avi

+2

De la documentation de StringBuffer: La classe StringBuilder devrait généralement être utilisée de préférence à celle-ci, car elle supporte toutes les mêmes opérations mais elle est plus rapide car elle n'effectue aucune synchronisation. – tgamblin

+0

Je travaillais beaucoup avec un environnement multi-thread, donc StringBuffer est venu naturellement dans mon esprit. –

7

Les messages précédents ont raison, StringBuilder/StringBuffer sont une solution. Mais, vous devez également vous demander si c'est une bonne idée de faire le remplacement sur les grandes chaînes en mémoire. J'ai souvent des manipulations de chaînes qui sont implémentées en tant que flux, donc au lieu de le remplacer dans la chaîne puis de l'envoyer à un OutputStream, je fais le remplacement au moment où j'envoie la chaîne au flux de sortie. Cela fonctionne beaucoup plus vite que tout remplacer.

Cela fonctionne beaucoup plus rapidement si vous souhaitez que ce remplacement implémente un mécanisme de modèle. Le streaming est toujours plus rapide car vous consommez moins de mémoire et si les clients sont lents, il vous suffit de générer à un rythme lent - donc il évolue beaucoup mieux.

+2

Pouvez-vous donner un exemple? –

1

Si vous avez un certain nombre de chaînes à remplacer (telles que des séquences d'échappement XML), en particulier lorsque les remplacements sont de longueur différente du modèle, l'algorithme de type FSM lexer semble être le plus efficace. dans un flux, où la sortie est incrémentalement construite.

Peut-être qu'un objet Matcher pourrait être utilisé pour le faire efficacement.

1

Obtenez le char[] du String et parcourez-le. Utilisez un StringBuilder temporaire.

Recherchez le motif que vous souhaitez remplacer pendant l'itération si vous ne trouvez pas le motif, écrivez les éléments que vous avez numérisés au StringBuilder, sinon écrivez le texte de remplacement au StringBuilder.

2

En ajoutant à la réponse @paxdiablo, voici un exemple d'implémentation d'un fichier replaceAll utilisant StringBuffers qui est ~ 3,7 fois plus rapide que String.replaceAll():

Code:

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(); 
    } 
} 

cas de test - la sortie est la suivante (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

Lorsque vous remplacez des caractères simples , considérez l'itération sur votre tableau de caractères mais remplacez les caractères en utilisant un (pré-créé) HashMap<Character, Character>().

J'utilise cette stratégie pour convertir une chaîne d'exposants entiers en caractères exposants unicode. Il est environ deux fois plus rapide que String.replace(char, char). Notez que l'heure associée à la création de la table de hachage n'est pas incluse dans cette comparaison.

3

Le code suivant est d'env. 30 fois plus rapide s'il n'y a pas de match et 5 fois plus vite s'il y a un match.

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(); 
} 
Questions connexes