web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de concaténer deux chaînes en Java?

Quel est le moyen le plus rapide de concaténer deux chaînes en Java?

c'est à dire 

String ccyPair = ccy1 + ccy2;

J'utilise cyPair comme clé dans une HashMap et elle est appelée dans une boucle très étroite pour récupérer des valeurs.

Quand je profile alors c'est le goulot d'étranglement

Java.lang.StringBuilder.append(StringBuilder.Java:119)  
Java.lang.StringBuilder.(StringBuilder.Java:93)
31
Dan

La raison pour laquelle ces routines apparaissent dans le test de performance est que le compilateur implémente votre "+" sous les couvertures.

Si vous avez vraiment besoin de la chaîne concaténée, vous devriez laisser le compilateur faire sa magie avec le "+". Si vous avez tous besoin d'une clé pour la recherche de carte, une classe de clés contenant les deux chaînes avec les implémentations appropriées de equals et hashMap pourrait être une bonne idée car elle évite l'étape de copie.

19

Beaucoup de théorie - temps pour la pratique!

private final String s1 = new String("1234567890");
private final String s2 = new String("1234567890");

Utilisation de lignes simples pour des boucles de 10 000 000, sur un Hotspot 64 bits réchauffé, 1.6.0_22 sur Intel Mac OS. 

par exemple

@Test public void testConcatenation() {
    for (int i = 0; i < COUNT; i++) {
        String s3 = s1 + s2;
    }
}

Avec les déclarations suivantes dans les boucles

String s3 = s1 + s2; 

1.33s

String s3 = new StringBuilder(s1).append(s2).toString();

1,28s

String s3 = new StringBuffer(s1).append(s2).toString();

1,92s

String s3 = s1.concat(s2);

0.70s

String s3 = "1234567890" + "1234567890";

0.0s

Donc concat est le gagnant clair, sauf si vous avez des chaînes statiques, auquel cas le compilateur aura déjà pris soin de vous.

38
Duncan McGregor

Je pense que la réponse a peut-être déjà été déterminée, mais je poste pour partager le code.

Une réponse courte, si la concaténation pure est tout ce que vous recherchez, est: String.concat (...)

Sortie:

ITERATION_LIMIT1: 1
ITERATION_LIMIT2: 10000000
s1: STRING1-1111111111111111111111
s2: STRING2-2222222222222222222222

iteration: 1
                                          null:    1.7 nanos
                                 s1.concat(s2):  106.1 nanos
                                       s1 + s2:  251.7 nanos
   new StringBuilder(s1).append(s2).toString():  246.6 nanos
    new StringBuffer(s1).append(s2).toString():  404.7 nanos
                 String.format("%s%s", s1, s2): 3276.0 nanos

Tests complete

Exemple de code:

package net.fosdal.scratch;

public class StringConcatenationPerformance {
    private static final int    ITERATION_LIMIT1    = 1;
    private static final int    ITERATION_LIMIT2    = 10000000;

    public static void main(String[] args) {
        String s1 = "STRING1-1111111111111111111111";
        String s2 = "STRING2-2222222222222222222222";
        String methodName;
        long startNanos, durationNanos;
        int iteration2;

        System.out.println("ITERATION_LIMIT1: " + ITERATION_LIMIT1);
        System.out.println("ITERATION_LIMIT2: " + ITERATION_LIMIT2);
        System.out.println("s1: " + s1);
        System.out.println("s2: " + s2);
        int iteration1 = 0;
        while (iteration1++ < ITERATION_LIMIT1) {
            System.out.println();
            System.out.println("iteration: " + iteration1);

            // method #0
            methodName = "null";
            iteration2 = 0;
            startNanos = System.nanoTime();
            while (iteration2++ < ITERATION_LIMIT2) {
                method0(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

            // method #1
            methodName = "s1.concat(s2)";
            iteration2 = 0;
            startNanos = System.nanoTime();
            while (iteration2++ < ITERATION_LIMIT2) {
                method1(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

            // method #2
            iteration2 = 0;
            startNanos = System.nanoTime();
            methodName = "s1 + s2";
            while (iteration2++ < ITERATION_LIMIT2) {
                method2(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

            // method #3
            iteration2 = 0;
            startNanos = System.nanoTime();
            methodName = "new StringBuilder(s1).append(s2).toString()";
            while (iteration2++ < ITERATION_LIMIT2) {
                method3(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

            // method #4
            iteration2 = 0;
            startNanos = System.nanoTime();
            methodName = "new StringBuffer(s1).append(s2).toString()";
            while (iteration2++ < ITERATION_LIMIT2) {
                method4(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

            // method #5
            iteration2 = 0;
            startNanos = System.nanoTime();
            methodName = "String.format(\"%s%s\", s1, s2)";
            while (iteration2++ < ITERATION_LIMIT2) {
                method5(s1, s2);
            }
            durationNanos = System.nanoTime() - startNanos;
            System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));

        }
        System.out.println();
        System.out.println("Tests complete");

    }

    public static String method0(String s1, String s2) {
        return "";
    }

    public static String method1(String s1, String s2) {
        return s1.concat(s2);
    }

    public static String method2(String s1, String s2) {
        return s1 + s2;
    }

    public static String method3(String s1, String s2) {
        return new StringBuilder(s1).append(s2).toString();
    }

    public static String method4(String s1, String s2) {
        return new StringBuffer(s1).append(s2).toString();
    }

    public static String method5(String s1, String s2) {
        return String.format("%s%s", s1, s2);
    }

}
17
sfosdal

Vous devez tester avec une chaîne générée au moment de l'exécution (comme UUID.randomUUID (). ToString ()) pas au moment de la compilation (comme "ma chaîne"). Mes résultats sont

plus:     118 ns
concat:    52 ns
builder1: 102 ns
builder2:  66 ns
buffer1:  119 ns
buffer2:   87 ns

avec cette implémentation:

private static long COUNT = 10000000;

public static void main(String[] args) throws Exception {
    String s1 = UUID.randomUUID().toString();
    String s2 = UUID.randomUUID().toString();
    for(String methodName : new String[] {
            "none", "plus", "concat", "builder1", "builder2", "buffer1", "buffer2"
    }) {
        Method method = ConcatPerformanceTest.class.getMethod(methodName, String.class, String.class);
        long time = System.nanoTime();
        for(int i = 0; i < COUNT; i++) {
            method.invoke((Object) null, s1, s2);
        }
        System.out.println(methodName + ": " + (System.nanoTime() - time)/COUNT + " ns");
    }
}

public static String none(String s1, String s2) {
    return null;
}

public static String plus(String s1, String s2) {
    return s1 + s2;
}

public static String concat(String s1, String s2) {
    return s1.concat(s2);
}

public static String builder1(String s1, String s2) {
    return new StringBuilder(s1).append(s2).toString();
}

public static String builder2(String s1, String s2) {
    return new StringBuilder(s1.length() + s2.length()).append(s1).append(s2).toString();
}

public static String buffer1(String s1, String s2) {
    return new StringBuffer(s1).append(s2).toString();
}

public static String buffer2(String s1, String s2) {
    return new StringBuffer(s1.length() + s2.length()).append(s1).append(s2).toString();
}
7
haba713

Pour la question dans le titre: String.concat sera généralement le moyen le plus rapide de concaténer deux Strings (mais notez nulls). Aucun tampon intermédiaire [surdimensionné] ou autre objet n'est impliqué. Étrangement, + est compilé en un code relativement inefficace impliquant StringBuilder.

Cependant, votre question soulève d’autres problèmes. La concaténation de chaînes pour générer des clés pour une carte est un "anti-idiome" courant. C'est un hack et sujet aux erreurs. Etes-vous sûr que la clé générée est unique? Restera-t-il unique après la maintenance de votre code pour certaines exigences encore inconnues? La meilleure approche consiste à créer une classe de valeur immuable pour la clé. Utiliser une classe List et générique Tuple est un bidouillage bâclé.

5
Tom Hawtin - tackline

Pour moi, la méthode concat3 ci-dessous est le moyen le plus rapide après avoir effectué des tests de performance sur mes machines Windows et Linux distantes: - Bien que je pense que les performances de concat1 dépendent de l’implémentation et de l’optimisation de la machine virtuelle Java et qu’elles soient meilleures dans les versions futures.

    public class StringConcat {

    public static void main(String[] args) {
        int run = 100 * 100 * 1000;
        long startTime, total = 0;

        final String a = "a";
        final String b = "assdfsaf";
        final String c = "aasfasfsaf";
        final String d = "afafafdaa";
        final String e = "afdassadf";

        startTime = System.currentTimeMillis();
        concat1(run, a, b, c, d, e);
        total = System.currentTimeMillis() - startTime;
        System.out.println(total);

        startTime = System.currentTimeMillis();
        concat2(run, a, b, c, d, e);
        total = System.currentTimeMillis() - startTime;
        System.out.println(total);

        startTime = System.currentTimeMillis();
        concat3(run, a, b, c, d, e);
        total = System.currentTimeMillis() - startTime;
        System.out.println(total);
    }

    private static void concat3(int run, String a, String b, String c, String d, String e) {
        for (int i = 0; i < run; i++) {
            String str = new StringBuilder(a.length() + b.length() + c.length() + d.length() + e.length()).append(a)
                    .append(b).append(c).append(d).append(e).toString();
        }
    }

    private static void concat2(int run, String a, String b, String c, String d, String e) {
        for (int i = 0; i < run; i++) {
            String str = new StringBuilder(a).append(b).append(c).append(d).append(e).toString();
        }
    }

    private static void concat1(int run, String a, String b, String c, String d, String e) {
        for (int i = 0; i < run; i++) {
            String str = a + b + c + d + e;
        }
    }
}
3
leoismyname

Peut-être devriez-vous créer une classe Pair au lieu de la concaténation?

public class Pair<T1, T2> {
    private T1 first;
    private T2 second;

    public static <U1,U2> Pair<U1,U2> create(U1 first, U2 second) {
        return new Pair<U1,U2>(U1,U2);
    }

    public Pair( ) {}

    public Pair( T1 first, T2 second ) {
        this.first = first;
        this.second = second;
    }

    public T1 getFirst( ) {
        return first;
    }

    public void setFirst( T1 first ) {
        this.first = first;
    }

    public T2 getSecond( ) {
        return second;
    }

    public void setSecond( T2 second ) {
        this.second = second;
    }

    @Override
    public String toString( ) {
        return "Pair [first=" + first + ", second=" + second + "]";
    }

    @Override
    public int hashCode( ) {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((first == null)?0:first.hashCode());
        result = prime * result + ((second == null)?0:second.hashCode());
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if ( this == obj )
            return true;
        if ( obj == null )
            return false;
        if ( getClass() != obj.getClass() )
            return false;
        Pair<?, ?> other = (Pair<?, ?>) obj;
        if ( first == null ) {
            if ( other.first != null )
                return false;
        }
        else if ( !first.equals(other.first) )
            return false;
        if ( second == null ) {
            if ( other.second != null )
                return false;
        }
        else if ( !second.equals(other.second) )
            return false;
        return true;
    }

}

Et utilisez ceci comme clé dans votre HashMap

Au lieu de HashMap<String,Whatever>, utilisez HashMap<Pair<String,String>,Whatever>

Dans votre boucle étroite au lieu de map.get( str1 + str2 ), vous utiliseriez map.get( Pair.create(str1,str2) ).

1
KitsuneYMG

Je recommanderais d'essayer la suggestion de Thorbjørn Ravn Andersens.

Si vous avez besoin des chaînes concaténées, en fonction de la longueur des deux parties, la création de l'instance de StringBuilder avec la taille requise peut être légèrement meilleure pour éviter la réallocation. Le constructeur par défaut de StringBuilder réserve 16 caractères dans l'implémentation actuelle, du moins sur ma machine. Ainsi, si la chaîne concaténée est plus longue que la taille de la mémoire tampon initiale, StringBuilder doit être réaffecté.

Essayez ceci et dites-nous ce que votre profileur a à dire à ce sujet:

StringBuilder ccyPair = new StringBuilder(ccy1.length()+ccy2.length());
ccyPair.append(ccy1); 
ccyPair.append(ccy2); 
1
Axel

Selon la spécification Java ( et depuis la toute première version de Java ), dans la section "Opérateur de concaténation de chaînes +", il est dit que:

Pour augmenter les performances de la concaténation de chaînes répétée, un fichier Java Le compilateur peut utiliser la classe StringBuffer ou une technique similaire à celle de Réduisez le nombre d'objets String intermédiaires créés par évaluation d'une expression

Donc, fondamentalement, l’utilisation du + operator ou du StringBuilder.append pour les variables est fondamentalement la même. 


Autre chose, je sais que dans votre question vous avez mentionné l’ajout de 2 chaînes seulement, mais gardez à l’esprit que l’ajout de 3 chaînes ou plus donnera des résultats différents:

J'ai utilisé un exemple légèrement modifié de @Duncan McGregor. J'ai 5 méthodes concaténant 2 à 6 chaînes en utilisant concat et 5 méthodes concaténant 2 à 6 chaînes en utilisant StringBuilder:

// Initialization
    private final String s1 = new String("1234567890");
    private final String s2 = new String("1234567890");
    private final String s3 = new String("1234567890");
    private final String s4 = new String("1234567890");
    private final String s5 = new String("1234567890");
    private final String s6 = new String("1234567890");

// testing the concat
    public void testConcatenation2stringsConcat(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = s1.concat(s2);
        }
    }
    public void testConcatenation3stringsConcat(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = s1.concat(s2).concat(s3);
        }
    }
    public void testConcatenation4stringsConcat(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = s1.concat(s2).concat(s3).concat(s4);
        }
    }
    public void testConcatenation5stringsConcat(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = s1.concat(s2).concat(s3).concat(s4).concat(s5);
        }
    }
    public void testConcatenation6stringsConcat(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = s1.concat(s2).concat(s3).concat(s4).concat(s5).concat(s6);
        }
    }

//testing the StringBuilder
    public void testConcatenation2stringsSB(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = new StringBuilder(s1).append(s2).toString();
        }
    }
    public void testConcatenation3stringsSB(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = new StringBuilder(s1).append(s2).append(s3).toString();
        }
    }
    public void testConcatenation4stringsSB(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).toString();
        }
    }
    public void testConcatenation5stringsSB(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).append(s5).toString();
        }
    }
    public void testConcatenation6stringsSB(int count) {
        for (int i = 0; i < count; i++) {
            String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).append(s5).append(s6).toString();
        }
    }

J'ai obtenu ces résultats (en secondes):

testConcatenation2stringsConcat: 0.018 |||||||||||||||| testConcatenation2stringsSB: 0.2 testConcatenation3stringsConcat: 0.35 |||||||||||||||||| testConcatenation3stringsSB: 0.25 testConcatenation4stringsConcat: 0.5 ||||||||||||||||||||| testConcatenation4stringsSB: 0.3 testConcatenation5stringsConcat: 0.67 ||||||||||||||||| testConcatenation5stringsSB: 0.38 testConcatenation5stringsConcat: 0.9 ||||||||||||||||||||| testConcatenation5stringsSB: 0.43

  • Vous pouvez constater que concat est plus rapide que StringBuilder uniquement lorsque Ne concatène que 2 chaînes.
  • Vérifiez que lors de l'ajout de plus en plus de chaînes, le temps résultant de StringBuilder augmente de plus en plus lentement.
  • Notez que la différence sera plus significative lorsque les chaînes Sont très longues
0
ihebiheb

Il s’agit ici d’une implémentation complète de la carte à sonde linéaire avec des clés doubles, valeur unique. Il devrait également surperformer Java.util.HashMap.

Attention, il est écrit dans les toutes premières heures de la journée, il peut donc contenir des bugs. S'il vous plaît n'hésitez pas à le modifier.

La solution doit battre n'importe quel wrapper, concat un à tout moment. L'absence d'allocation sur get/put rend également la carte polyvalente rapide.

J'espère que cela résout le problème. (Le code vient avec quelques tests simples qui ne sont pas nécessaires)


package bestsss.util;


@SuppressWarnings("unchecked")
public class DoubleKeyMap<K1, K2, V> {
    private static final int MAX_CAPACITY =  1<<29; 

    private static final Object TOMBSTONE = new String("TOMBSTONE");

    Object[] kvs; 
    int[] hashes;
    int count = 0;

    final int rehashOnProbes;   

    public DoubleKeyMap(){
        this(8, 5);
    }

    public DoubleKeyMap(int capacity, int rehashOnProbes){
        capacity = nextCapacity(Math.max(2, capacity-1));
        if (rehashOnProbes>capacity){
            throw new IllegalArgumentException("rehashOnProbes too high");
        }
        hashes = new int[capacity];
        kvs = new Object[kvsIndex(capacity)];       
        count = 0;
        this.rehashOnProbes = rehashOnProbes;
    }

    private static int nextCapacity(int c) {
        int n = Integer.highestOneBit(c)<<1;
        if (n<0 || n>MAX_CAPACITY){
            throw new Error("map too large");
        }
        return n;
    }

    //alternatively this method can become non-static, protected and overriden, the perfoamnce can drop a little
    //but if better spread of the lowest bit is possible, all good and proper
    private static<K1, K2> int hash(K1 key1, K2 key2){
        //spread more, if need be
        int h1 = key1.hashCode();
        int h2 = key2.hashCode();
        return h1+ (h2<<4) + h2; //h1+h2*17
    }

    private static int kvsIndex(int baseIdx){
        int idx = baseIdx;  
        idx+=idx<<1;//idx*3
        return idx;
    }

    private int baseIdx(int hash){
        return hash & (hashes.length-1);
    }

    public V get(K1 key1, K2 key2){
        final int hash = hash(key1, key2);


        final int[] hashes = this.hashes;
        final Object[] kvs = this.kvs;
        final int mask = hashes.length-1;

        for(int base = baseIdx(hash);;base=(base+1)&mask){
            int k = kvsIndex(base);
            K1 k1 = (K1) kvs[k];
            if (k1==null)
                return null;//null met; no such value

            Object value;
            if (hashes[base]!=hash || TOMBSTONE==(value=kvs[k+2]))
                continue;//next

            K2 k2 = (K2) kvs[k+1];
            if ( (key1==k1 || key1.equals(k1)) && (key2==k2 || key2.equals(k2)) ){
                return (V) value;
            }
        }
    }
    public boolean contains(K1 key1, K2 key2){
        return get(key1, key2)!=null;
    }

    public boolean containsValue(final V value){
        final Object[] kvs = this.kvs;
        if (value==null)
            return false;

        for(int i=0;i<kvs.length;i+=3){
            Object v = kvs[2];
            if (v==null || v==TOMBSTONE)
                continue;
            if (value==v || value.equals(v))
                return true;
        }
        return false;
    }

    public V put(K1 key1, K2 key2, V value){
        int hash = hash(key1, key2);
        return doPut(key1, key2, value, hash);
    }
    public V remove(K1 key1, K2 key2){
        int hash = hash(key1, key2);
        return doPut(key1, key2, null, hash);   
    }

    //note, instead of remove a TOMBSTONE is used to mark the deletion
    //this may leak keys but deletion doesn't need to shift the array like in Knuth 6.4
    protected V doPut(final K1 key1, final K2 key2, Object value, final int hash){
        //null value -> remove
        int probes = 0;
        final int[] hashes = this.hashes;
        final Object[] kvs = this.kvs;
        final int mask = hashes.length-1;

        //conservative resize: when too many probes and the count is greater than the half of the capacity
        for(int base = baseIdx(hash);probes<rehashOnProbes || count<(mask>>1);base=(base+1)&mask, probes++){
            final int k = kvsIndex(base);
            K1 k1 = (K1) kvs[k];
            K2 k2;
            //find a gap, or resize
            Object  old  = kvs[k+2];
            final boolean emptySlot = k1==null || (value!=null && old==TOMBSTONE); 
            if (emptySlot || (
                    hashes[base] == hash &&
                    (k1==key1  || k1.equals(key1)) &&
                    ((k2=(K2) kvs[k+1])==key2 || k2.equals(key2))) 
            ){

                if (value==null){//remove()
                    if (emptySlot)
                        return null;//not found, and no value ->nothing to do

                    value = TOMBSTONE;
                    count-=2;//offset the ++later
                }

                if (emptySlot){//new entry, update keys
                    hashes[base] = hash;                
                    kvs[k] = key1;
                    kvs[k+1] = key2;
                }//else -> keys and hash are equal


                if (old==TOMBSTONE) 
                    old=null;

                kvs[k+2] = value;
                count++;

                return (V) old;
            }
        }
        resize();
        return doPut(key1, key2, value, hash);//hack w/ recursion, after the resize
    }

    //optimized version during resize, doesn't check equals which is the slowest part   
    protected void doPutForResize(K1 key1, K2 key2, V value, final int hash){
        final int[] hashes = this.hashes;
        final Object[] kvs = this.kvs;
        final int mask = hashes.length-1;

        //find the 1st gap and insert there
        for(int base = baseIdx(hash);;base=(base+1)&mask){//it's ensured, no equal keys exist, so skip equals part
            final int k = kvsIndex(base);
            K1 k1 = (K1) kvs[k];
            if (k1!=null) 
                continue;

            hashes[base] = hash;                
            kvs[k] = key1;
            kvs[k+1] = key2;
            kvs[k+2] = value;
            return;
        }
    }

    //resizes the map by doubling the capacity, 
    //the method uses altervative varian of put that doesn't check equality, or probes; just inserts at a gap
    protected void resize(){        
        final int[] hashes = this.hashes;
        final Object[] kvs = this.kvs;
        final int capacity = nextCapacity(hashes.length);

        this.hashes = new int[capacity];
        this.kvs = new Object[kvsIndex(capacity)];

        for (int i=0;i<hashes.length; i++){
            int k = kvsIndex(i);
            K1 key1 = (K1) kvs[k];
            Object value = kvs[k+2];
            if (key1!=null && TOMBSTONE!=value){
                K2 key2 = (K2) kvs[k+1];
                doPutForResize(key1, key2, (V) value, hashes[i]);
            }
        }   
    }

    public static void main(String[] args) {
        DoubleKeyMap<String, String, Integer> map = new DoubleKeyMap<String, String, Integer>(4,2);
        map.put("eur/usd", "usd/jpy", 1);
        map.put("eur/usd", "usd/jpy", 2);

        map.put("eur/jpy", "usd/jpy", 3);

        System.out.println(map.get("eur/jpy", "usd/jpy"));
        System.out.println(map.get("eur/usd", "usd/jpy"));
        System.out.println("======");

        map.remove("eur/usd", "usd/jpy");
        System.out.println(map.get("eur/jpy", "usd/jpy"));
        System.out.println(map.get("eur/usd", "usd/jpy"));
        System.out.println("======");
        testResize();
    }
    static void testResize(){
        DoubleKeyMap<String, Integer, Integer> map = new DoubleKeyMap<String, Integer, Integer>(18, 17);
        long s = 0;
        String pref="xxx";

        for (int i=0;i<14000;i++){
            map.put(pref+i, i, i);

            if ((i&1)==1)
                map.remove(pref+i, i);
            else
                s+=i;
        }
        System.out.println("sum: "+s);
        long sum = 0;

        for (int i=0;i<14000;i++){
            Integer n = map.get(pref+i, i);
            if (n!=null && n!=i){
                throw new AssertionError(); 
            }
            if (n!=null){
                System.out.println(n);
                sum+=n;
            }
        }
        System.out.println("1st sum: "+s);
        System.out.println("2nd sum: "+sum);


    }
}
0
bestsss

N'oubliez pas que si vous concaténez des millions de chaînes, string.concat générera probablement des millions de nouvelles références d'objet de chaîne. Cela aura une utilisation accrue du processeur.

0
ThatDataGuy
StringBuffer ccyPair =  new StringBuffer();      
ccyPair.append("ccy1").append("ccy2"); 

Avez-vous déjà essayé d’utiliser un tampon de chaîne puis d’utiliser un profileur pour déterminer le goulot d’étranglement? Essayez-le et voyez ce qui se passe.

0
Deepak

La réponse de @Duncan McGregor donne des chiffres de référence pour un exemple particulier (tailles de la chaîne d'entrée) et une version de la JVM. Dans ce cas, il semble que String.concat() soit le gagnant par un facteur important. Ce résultat peut ou non généraliser.

À part: cela me surprend! J'aurais pensé que les rédacteurs du compilateur auraient choisi d'utiliser String.concat dans les cas où cela serait probablement plus rapide. L'explication est dans l'évaluation de ce rapport de bogue ... et est enracinée dans la définition de l'opérateur de concaténation de chaînes. 

(Si un opérande de type chaîne de + est null, le JLS indique que la chaîne "null" est utilisée à sa place. Cela ne fonctionnerait pas si le code généré par s + s2 sous la forme s.concat(s2) et s ou s2 était réellement null; Et le cas de s == null signifie qu'une version alternative de concat ne résout pas le problème de NPE.)


Cependant, la réponse de @winden m'a donné une idée d'une solution alternative évitant le recours à la concaténation de chaînes.

Si les concaténations de ccy1 et ccy2 ne servent qu'à associer deux clés, vous obtiendrez peut-être de meilleures performances en définissant une classe de table de hachage spéciale qui prend deux clés au lieu d'une. Il aurait des opérations comme:

    public Object get(String key1, String key2) ...

    public void put(String key1, String key2, Object value) ...

L'effet ressemblerait à un Map<Pair<String, String>, Object> (voir la réponse de @ KitsuneYMG), sauf qu'il n'est pas nécessaire de créer des objets Pair<String, String> à chaque fois que vous souhaitez créer une get ou put. L'inconvénient est:

  • vous devez implémenter une nouvelle classe de table de hachage, et
  • la nouvelle classe ne sera pas conforme à l'interface Map.

Normalement, je ne recommanderais pas cela. Toutefois, si la concaténation de chaînes et la recherche de cartes constituent réellement un goulot d'étranglement, une table de hachage multi-clé personnalisée peut vous permettre de gagner beaucoup de temps.

0
Stephen C

Vous pouvez peut-être contourner le problème en calculant les hachages des deux chaînes individuellement, puis en les combinant, éventuellement avec une fonction de hachage distincte qui fonctionne sur des entiers?

Quelque chose comme:

int h1 = ccy1.hashCode(), h2 = ccy2.hashCode(), h = h1 ^ h2;

Cela pourrait bien être plus rapide, car concaténer des chaînes uniquement pour calculer le hash de la concaténation semble inutile.

Notez que ce qui précède combine les deux hachages avec binary-XOR (l'opérateur ^), ce qui fonctionne souvent, mais vous voudrez peut-être approfondir cette question.

0
unwind

Ok, alors quelle est votre question? Rien à faire: si vous devez concaténer des chaînes, faites-le. C'est bien que vous ayez profilé votre code. Vous pouvez maintenant voir que l'opérateur de concaténation de chaînes + utilise automatiquement la méthode append () de StringBuilder. 

StringBuilder ccyPair = new StringBuilder(ccy1)
ccyPair.append(ccy2);

ne vous donne pas d'avantages sérieux.

Le seul moyen sérieux d’optimiser votre code est probablement de modifier votre conception afin d’omettre la concaténation. Mais ne le faites que si vous en avez vraiment besoin, c’est-à-dire que la concaténation prend une partie importante du temps processeur.

0
AlexR