173 votes

Moyen le plus rapide pour parcourir tous les caractères dans une chaîne

En Java, ce qui serait le moyen le plus rapide pour parcourir tous les caractères dans une Chaîne, ceci:

String str = "a really, really long string";
for (int i = 0, n = str.length(); i < n; i++) {
    char c = str.charAt(i);
}

Ou ceci:

char[] chars = str.toCharArray();
for (int i = 0, n = chars.length; i < n; i++) {
    char c = chars[i];
}

EDIT :

Ce que je voudrais savoir c'est si le coût de reprises de l'appel de la charAt méthode pendant une longue itération finit par être inférieur ou supérieur au coût de la réalisation d'un seul appel à l' toCharArray au début et ensuite accéder directement au tableau lors de l'itération.

Ce serait super si quelqu'un pouvait fournir une solide référence pour les différentes longueurs de chaîne, ayant à l'esprit JIT temps d'échauffement, JVM heure de démarrage, etc. et pas seulement la différence entre les deux appels d' System.currentTimeMillis().

369voto

Saint Hill Points 2285

Tout dépend de la longueur de l' String inspecté. Si, comme la question dit, c'est pour de longues chaînes, le moyen le plus rapide pour inspecter la chaîne est d'utiliser la réflexion pour accéder à la sauvegarde char[] de la chaîne.

Entièrement randomisé de référence avec JDK 8 (win32 et win64) sur un 64, AMD Phenom II 4 core 955 @ 3.2 GHZ (en mode client et serveur de mode) avec 9 différentes techniques (voir ci-dessous!) montre que l'utilisation d' String.charAt(n) est le plus rapide pour les petites chaînes et que l'utilisation d' reflection pour accéder à la Chaîne de la sauvegarde de tableau est presque deux fois plus rapide pour les grandes chaînes.

L'EXPÉRIENCE

  • 9 les différentes techniques d'optimisation sont essayé.

  • Toutes les chaînes de caractères contenus sont randomisés

  • Les test sont fait pour les formats de chaîne dans des multiples de deux en commençant par 0,1,2,4,8,16 etc.

  • Les tests sont fait 1000 fois par taille de la chaîne

  • Les tests sont mélangés dans un ordre aléatoire à chaque fois. En d'autres termes, les tests sont effectués dans un ordre aléatoire à chaque fois qu'ils sont fait, plus de 1000 fois.

  • L'ensemble de la suite de test est fait avant, et en arrière, à montrer l'effet de la JVM de chauffe sur l'optimisation et de la fois.

  • Toute la suite est fait deux fois, une fois dans -client mode et l'autre en -server mode.

CONCLUSIONS

-en mode client (32 bits)

Pour les chaînes 1 à 256 caractères, appelant string.charAt(i) victoires avec une moyenne de traitement de 13,4 millions de dollars à 588 millions de caractères par seconde.

Aussi, il est de 5,5% plus rapide (client) et 13,9% (serveur) comme ceci:

    for (int i = 0; i < data.length(); i++) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }

que comme ça avec un local de la finale, de longueur variable:

    final int len = data.length();
    for (int i = 0; i < len; i++) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }

Pendant de longues chaînes, 512 256 caractères de longueur, à l'aide de réflexion pour accéder à la Chaîne du support de tableau est plus rapide. Cette technique est presque deux fois plus vite que la Chaîne.charAt(i) (178% plus rapide). La vitesse moyenne au cours de cette gamme a été 1,111 milliard de caractères par seconde.

Le Champ doit être obtenu à l'avance et ensuite il peut être ré-utilisé dans la bibliothèque sur des cordes différentes. Il est intéressant de noter, à la différence du code ci-dessus, avec accès sur le Terrain, c'est 9% plus rapide d'avoir un local de la finale, de longueur variable que l'utilisation de 'caractères.la longueur de la boucle de contrôle. Voici comment accéder au Terrain peut être configuré comme le plus rapide:

   final Field field = String.class.getDeclaredField("value");
   field.setAccessible(true);

   try {
       final char[] chars = (char[]) field.get(data);
       final int len = chars.length;
       for (int i = 0; i < len; i++) {
           if (chars[i] <= ' ') {
               doThrow();
           }
       }
       return len;
   } catch (Exception ex) {
       throw new RuntimeException(ex);
   }

Observations particulières à formuler sur -le mode serveur

Accéder au terrain de départ de gagner après 32 caractères les chaînes de longueur en mode serveur sur une version 64 bits de Java machine sur mon AMD 64 de la machine. Qui n'avait pas vu jusqu'à 512 caractères en mode client.

Également à noter, je pense que lorsque j'étais en train de JDK 8 (32 bits) en mode serveur, la performance globale a été de 7% plus lent pour les petites et grandes chaînes. C'était de construire avec 121 Déc 2013 du JDK 8 libération anticipée. Donc, pour l'instant, il semble que 32 bits mode de serveur est plus lent que le 32 bits en mode client.

Cela dit ... il semble que la seule mode serveur qui vaut de l'invocation est sur un ordinateur 64 bits. Sinon, il fait entrave à la performance.

Pour 32 bits s'exécutant en -server mode sur un AMD64, je peux dire ceci:

  1. Chaîne de caractères.charAt(i) est le gagnant clair dans l'ensemble. Bien qu'entre les tailles de 8 à 512 caractères, il y avait des gagnants parmi les "nouveaux ""réutiliser" et "champ".
  2. Chaîne de caractères.charAt(i) est de 45% plus rapide en mode client
  3. Le terrain est deux fois plus rapide pour les grandes Chaînes de caractères en mode client.

Aussi important de dire, de Chaîne.caractères() (Flux et la version parallèle) sont un buste. Beaucoup plus lent que de toute autre manière. L' Streams API est plutôt lent pour effectuer générale des opérations de la chaîne.

Liste De Souhaits

Java Chaîne pourrait avoir prédicat d'accepter optimisée des méthodes telles que contient(prédicat), forEach(le consommateur), forEachWithIndex(le consommateur). Ainsi, sans la nécessité pour l'utilisateur de connaître la longueur de la ou des appels répétés à la Chaîne de méthodes, celles-ci pourraient aider les bibliothèques d'analyse beep-beep beep speedup.

Continuer de rêver :)

Heureux Les Chaînes!

~SH

Le test utilisé en 9 méthodes de test de la chaîne de la présence d'espaces:

"charAt1" -- VÉRIFIER le CONTENU de LA CHAÎNE de LA MANIÈRE HABITUELLE:

int charAtMethod1(final String data) {
    final int len = data.length();
    for (int i = 0; i < len; i++) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }
    return len;
}

"charAt2" -- MÊMES QUE ci-DESSUS, MAIS UTILISER des chaînes de caractères.longueur() au LIEU DE FAIRE UNE FINALE LOCALE int POUR LA Longueur

int charAtMethod2(final String data) {
    for (int i = 0; i < data.length(); i++) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }
    return data.length();
}

"le ruisseau" -- UTILISER LE NOUVEAU JAVA 8 de la Chaîne de IntStream ET de PASSER à UN PRÉDICAT D'effectuer LA VÉRIFICATION

int streamMethod(final String data, final IntPredicate predicate) {
    if (data.chars().anyMatch(predicate)) {
        doThrow();
    }
    return data.length();
}

"streamPara" -- MÊME QUE ci-DESSUS, MAIS OH-LA-LA - PARALLÈLE!!!

// avoid this at all costs
int streamParallelMethod(final String data, IntPredicate predicate) {
    if (data.chars().parallel().anyMatch(predicate)) {
        doThrow();
    }
    return data.length();
}

"réutilisation" -- RECHARGE RÉUTILISABLES char[] AVEC LES CHAÎNES de CONTENU

int reuseBuffMethod(final char[] reusable, final String data) {
    final int len = data.length();
    data.getChars(0, len, reusable, 0);
    for (int i = 0; i < len; i++) {
        if (reusable[i] <= ' ') {
            doThrow();
        }
    }
    return len;
}

"new1" -- OBTENIR UNE NOUVELLE COPIE DU char[] à PARTIR de LA CHAÎNE

int newMethod1(final String data) {
    final int len = data.length();
    final char[] copy = data.toCharArray();
    for (int i = 0; i < len; i++) {
        if (copy[i] <= ' ') {
            doThrow();
        }
    }
    return len;
}

"new2" -- MÊME QUE ci-DESSUS, MAIS l'UTILISATION de "FOR-each"

int newMethod2(final String data) {
    for (final char c : data.toCharArray()) {
        if (c <= ' ') {
            doThrow();
        }
    }
    return data.length();
}

"champ1" -- FANTAISIE!! OBTENIR le CHAMP DE l'ACCÈS À LA CHAÎNE INTERNE DE char[]

int fieldMethod1(final Field field, final String data) {
    try {
        final char[] chars = (char[]) field.get(data);
        final int len = chars.length;
        for (int i = 0; i < len; i++) {
            if (chars[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

"champ2", les MÊMES QUE ci-DESSUS, MAIS l'UTILISATION de "FOR-each"

int fieldMethod2(final Field field, final String data) {
    final char[] chars;
    try {
        chars = (char[]) field.get(data);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
    for (final char c : chars) {
        if (c <= ' ') {
            doThrow();
        }
    }
    return chars.length;
}

RÉSULTATS COMPOSITES, POUR le CLIENT, -client MODE (en avant et en arrière des tests combinés)

Remarque: le mode client avec Java 32 bits et en mode serveur avec Java en 64 bits sont les mêmes que ci-dessous sur mon AMD64 machine.

Size     WINNER  charAt1 charAt2  stream streamPar   reuse    new1    new2  field1  field2
1        charAt    77.0     72.0   462.0     584.0   127.5    89.5    86.0   159.5   165.0
2        charAt    38.0     36.5   284.0   32712.5    57.5    48.3    50.3    89.0    91.5
4        charAt    19.5     18.5   458.6    3169.0    33.0    26.8    27.5    54.1    52.6
8        charAt     9.8      9.9   100.5    1370.9    17.3    14.4    15.0    26.9    26.4
16       charAt     6.1      6.5    73.4     857.0     8.4     8.2     8.3    13.6    13.5
32       charAt     3.9      3.7    54.8     428.9     5.0     4.9     4.7     7.0     7.2
64       charAt     2.7      2.6    48.2     232.9     3.0     3.2     3.3     3.9     4.0
128      charAt     2.1      1.9    43.7     138.8     2.1     2.6     2.6     2.4     2.6
256      charAt     1.9      1.6    42.4      90.6     1.7     2.1     2.1     1.7     1.8
512      field1     1.7      1.4    40.6      60.5     1.4     1.9     1.9     1.3     1.4
1,024    field1     1.6      1.4    40.0      45.6     1.2     1.9     2.1     1.0     1.2
2,048    field1     1.6      1.3    40.0      36.2     1.2     1.8     1.7     0.9     1.1
4,096    field1     1.6      1.3    39.7      32.6     1.2     1.8     1.7     0.9     1.0
8,192    field1     1.6      1.3    39.6      30.5     1.2     1.8     1.7     0.9     1.0
16,384   field1     1.6      1.3    39.8      28.4     1.2     1.8     1.7     0.8     1.0
32,768   field1     1.6      1.3    40.0      26.7     1.3     1.8     1.7     0.8     1.0
65,536   field1     1.6      1.3    39.8      26.3     1.3     1.8     1.7     0.8     1.0
131,072  field1     1.6      1.3    40.1      25.4     1.4     1.9     1.8     0.8     1.0
262,144  field1     1.6      1.3    39.6      25.2     1.5     1.9     1.9     0.8     1.0

RÉSULTATS COMPOSITES POUR le SERVEUR -server MODE (en avant et en arrière des tests combinés)

Remarque: ce test est pour Java 32 bits s'exécutant en mode serveur sur un AMD64. Le mode de serveur pour Java 64 bits est le même que Java 32 bits en mode client, sauf que l'accès à un Champ de départ de gagner après 32 taille des caractères.

Size     WINNER  charAt1 charAt2  stream streamPar   reuse    new1    new2  field1  field2
1        charAt     74.5    95.5   524.5     783.0    90.5   102.5    90.5   135.0   151.5
2        charAt     48.5    53.0   305.0   30851.3    59.3    57.5    52.0    88.5    91.8
4        charAt     28.8    32.1   132.8    2465.1    37.6    33.9    32.3    49.0    47.0
8          new2     18.0    18.6    63.4    1541.3    18.5    17.9    17.6    25.4    25.8
16         new2     14.0    14.7   129.4    1034.7    12.5    16.2    12.0    16.0    16.6
32         new2      7.8     9.1    19.3     431.5     8.1     7.0     6.7     7.9     8.7
64        reuse      6.1     7.5    11.7     204.7     3.5     3.9     4.3     4.2     4.1
128       reuse      6.8     6.8     9.0     101.0     2.6     3.0     3.0     2.6     2.7
256      field2      6.2     6.5     6.9      57.2     2.4     2.7     2.9     2.3     2.3
512       reuse      4.3     4.9     5.8      28.2     2.0     2.6     2.6     2.1     2.1
1,024    charAt      2.0     1.8     5.3      17.6     2.1     2.5     3.5     2.0     2.0
2,048    charAt      1.9     1.7     5.2      11.9     2.2     3.0     2.6     2.0     2.0
4,096    charAt      1.9     1.7     5.1       8.7     2.1     2.6     2.6     1.9     1.9
8,192    charAt      1.9     1.7     5.1       7.6     2.2     2.5     2.6     1.9     1.9
16,384   charAt      1.9     1.7     5.1       6.9     2.2     2.5     2.5     1.9     1.9
32,768   charAt      1.9     1.7     5.1       6.1     2.2     2.5     2.5     1.9     1.9
65,536   charAt      1.9     1.7     5.1       5.5     2.2     2.4     2.4     1.9     1.9
131,072  charAt      1.9     1.7     5.1       5.4     2.3     2.5     2.5     1.9     1.9
262,144  charAt      1.9     1.7     5.1       5.1     2.3     2.5     2.5     1.9     1.9

PLEIN DE CODE DE PROGRAMME EXÉCUTABLE

(essai sur Java 7 et les versions antérieures, retirez les deux flux de tests)

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.IntPredicate;

/**
 * @author Saint Hill <http://stackoverflow.com/users/1584255/saint-hill>
 */
public final class TestStrings {

    // we will not test strings longer than 512KM
    final int MAX_STRING_SIZE = 1024 * 256;

    // for each string size, we will do all the tests
    // this many times
    final int TRIES_PER_STRING_SIZE = 1000;

    public static void main(String[] args) throws Exception {
        new TestStrings().run();
    }

    void run() throws Exception {

        // double the length of the data until it reaches MAX chars long
        // 0,1,2,4,8,16,32,64,128,256 ... 
        final List<Integer> sizes = new ArrayList<>();
        for (int n = 0; n <= MAX_STRING_SIZE; n = (n == 0 ? 1 : n * 2)) {
            sizes.add(n);
        }

        // CREATE RANDOM (FOR SHUFFLING ORDER OF TESTS)
        final Random random = new Random();

        System.out.println("Rate in nanoseconds per character inspected.");
        System.out.printf("==== FORWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);

        printHeadings(TRIES_PER_STRING_SIZE, random);

        for (int size : sizes) {
            reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));
        }

        // reverse order or string sizes
        Collections.reverse(sizes);

        System.out.println("");
        System.out.println("Rate in nanoseconds per character inspected.");
        System.out.printf("==== BACKWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);

        printHeadings(TRIES_PER_STRING_SIZE, random);

        for (int size : sizes) {
            reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));

        }
    }

    ///
    ///
    ///  METHODS OF CHECKING THE CONTENTS
    ///  OF A STRING. ALWAYS CHECKING FOR
    ///  WHITESPACE (CHAR <=' ')
    ///  
    ///
    // CHECK THE STRING CONTENTS
    int charAtMethod1(final String data) {
        final int len = data.length();
        for (int i = 0; i < len; i++) {
            if (data.charAt(i) <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // SAME AS ABOVE BUT USE String.length()
    // instead of making a new final local int 
    int charAtMethod2(final String data) {
        for (int i = 0; i < data.length(); i++) {
            if (data.charAt(i) <= ' ') {
                doThrow();
            }
        }
        return data.length();
    }

    // USE new Java-8 String's IntStream
    // pass it a PREDICATE to do the checking
    int streamMethod(final String data, final IntPredicate predicate) {
        if (data.chars().anyMatch(predicate)) {
            doThrow();
        }
        return data.length();
    }

    // OH LA LA - GO PARALLEL!!!
    int streamParallelMethod(final String data, IntPredicate predicate) {
        if (data.chars().parallel().anyMatch(predicate)) {
            doThrow();
        }
        return data.length();
    }

    // Re-fill a resuable char[] with the contents
    // of the String's char[]
    int reuseBuffMethod(final char[] reusable, final String data) {
        final int len = data.length();
        data.getChars(0, len, reusable, 0);
        for (int i = 0; i < len; i++) {
            if (reusable[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // Obtain a new copy of char[] from String
    int newMethod1(final String data) {
        final int len = data.length();
        final char[] copy = data.toCharArray();
        for (int i = 0; i < len; i++) {
            if (copy[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // Obtain a new copy of char[] from String
    // but use FOR-EACH
    int newMethod2(final String data) {
        for (final char c : data.toCharArray()) {
            if (c <= ' ') {
                doThrow();
            }
        }
        return data.length();
    }

    // FANCY!
    // OBTAIN FIELD FOR ACCESS TO THE STRING'S
    // INTERNAL CHAR[]
    int fieldMethod1(final Field field, final String data) {
        try {
            final char[] chars = (char[]) field.get(data);
            final int len = chars.length;
            for (int i = 0; i < len; i++) {
                if (chars[i] <= ' ') {
                    doThrow();
                }
            }
            return len;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    // same as above but use FOR-EACH
    int fieldMethod2(final Field field, final String data) {
        final char[] chars;
        try {
            chars = (char[]) field.get(data);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        for (final char c : chars) {
            if (c <= ' ') {
                doThrow();
            }
        }
        return chars.length;
    }

    /**
     *
     * Make a list of tests. We will shuffle a copy of this list repeatedly
     * while we repeat this test.
     *
     * @param data
     * @return
     */
    List<Jobber> makeTests(String data) throws Exception {
        // make a list of tests
        final List<Jobber> tests = new ArrayList<Jobber>();

        tests.add(new Jobber("charAt1") {
            int check() {
                return charAtMethod1(data);
            }
        });

        tests.add(new Jobber("charAt2") {
            int check() {
                return charAtMethod2(data);
            }
        });

        tests.add(new Jobber("stream") {
            final IntPredicate predicate = new IntPredicate() {
                public boolean test(int value) {
                    return value <= ' ';
                }
            };

            int check() {
                return streamMethod(data, predicate);
            }
        });

        tests.add(new Jobber("streamPar") {
            final IntPredicate predicate = new IntPredicate() {
                public boolean test(int value) {
                    return value <= ' ';
                }
            };

            int check() {
                return streamParallelMethod(data, predicate);
            }
        });

        // Reusable char[] method
        tests.add(new Jobber("reuse") {
            final char[] cbuff = new char[MAX_STRING_SIZE];

            int check() {
                return reuseBuffMethod(cbuff, data);
            }
        });

        // New char[] from String
        tests.add(new Jobber("new1") {
            int check() {
                return newMethod1(data);
            }
        });

        // New char[] from String
        tests.add(new Jobber("new2") {
            int check() {
                return newMethod2(data);
            }
        });

        // Use reflection for field access
        tests.add(new Jobber("field1") {
            final Field field;

            {
                field = String.class.getDeclaredField("value");
                field.setAccessible(true);
            }

            int check() {
                return fieldMethod1(field, data);
            }
        });

        // Use reflection for field access
        tests.add(new Jobber("field2") {
            final Field field;

            {
                field = String.class.getDeclaredField("value");
                field.setAccessible(true);
            }

            int check() {
                return fieldMethod2(field, data);
            }
        });

        return tests;
    }

    /**
     * We use this class to keep track of test results
     */
    abstract class Jobber {

        final String name;
        long nanos;
        long chars;
        long runs;

        Jobber(String name) {
            this.name = name;
        }

        abstract int check();

        final double nanosPerChar() {
            double charsPerRun = chars / runs;
            long nanosPerRun = nanos / runs;
            return charsPerRun == 0 ? nanosPerRun : nanosPerRun / charsPerRun;
        }

        final void run() {
            runs++;
            long time = System.nanoTime();
            chars += check();
            nanos += System.nanoTime() - time;
        }
    }

    // MAKE A TEST STRING OF RANDOM CHARACTERS A-Z
    private String makeTestString(int testSize, char start, char end) {
        Random r = new Random();
        char[] data = new char[testSize];
        for (int i = 0; i < data.length; i++) {
            data[i] = (char) (start + r.nextInt(end));
        }
        return new String(data);
    }

    // WE DO THIS IF WE FIND AN ILLEGAL CHARACTER IN THE STRING
    public void doThrow() {
        throw new RuntimeException("Bzzzt -- Illegal Character!!");
    }

    /**
     * 1. get random string of correct length 2. get tests (List<Jobber>) 3.
     * perform tests repeatedly, shuffling each time
     */
    List<Jobber> test(int size, int tries, Random random) throws Exception {
        String data = makeTestString(size, 'A', 'Z');
        List<Jobber> tests = makeTests(data);
        List<Jobber> copy = new ArrayList<>(tests);
        while (tries-- > 0) {
            Collections.shuffle(copy, random);
            for (Jobber ti : copy) {
                ti.run();
            }
        }
        // check to make sure all char counts the same
        long runs = tests.get(0).runs;
        long count = tests.get(0).chars;
        for (Jobber ti : tests) {
            if (ti.runs != runs && ti.chars != count) {
                throw new Exception("Char counts should match if all correct algorithms");
            }
        }
        return tests;
    }

    private void printHeadings(final int TRIES_PER_STRING_SIZE, final Random random) throws Exception {
        System.out.print("  Size");
        for (Jobber ti : test(0, TRIES_PER_STRING_SIZE, random)) {
            System.out.printf("%9s", ti.name);
        }
        System.out.println("");
    }

    private void reportResults(int size, List<Jobber> tests) {
        System.out.printf("%6d", size);
        for (Jobber ti : tests) {
            System.out.printf("%,9.2f", ti.nanosPerChar());
        }
        System.out.println("");
    }
}

14voto

Buhake Sindi Points 38654

Il s’agit de micro-optimisation juste que vous ne devriez pas vous soucier.

vous retourne une copie de tableaux de caractères (dans le JDK, il retourne une copie de caractères en appelant ).

Other than that, `` vérifie uniquement si l’indice est en effet dans les limites et retourne un caractère dans l’index de tableau.

L’un ne crée pas de mémoire supplémentaire dans la JVM.

9voto

ceklock Points 1186

Juste pour la curiosité et à la comparer avec la Sainte Colline de réponse.

Si vous avez besoin de processus lourds de données, vous ne devez pas utiliser la JVM en mode client. Mode Client n'est pas fait pour faire des optimisations.

Nous allons comparer les résultats de @Saint Hill repères à l'aide d'une JVM en mode Client et Serveur de mode.

Core2Quad Q6600 G0 @ 2.4GHz
JavaSE 1.7.0_40

Voir aussi: les vraies différences entre "java server" et "java -client"?


MODE CLIENT:

len =      2:    111k charAt(i),  105k cbuff[i],   62k new[i],   17k field access.   (chars/ms) 
len =      4:    285k charAt(i),  166k cbuff[i],  114k new[i],   43k field access.   (chars/ms) 
len =      6:    315k charAt(i),  230k cbuff[i],  162k new[i],   69k field access.   (chars/ms) 
len =      8:    333k charAt(i),  275k cbuff[i],  181k new[i],   85k field access.   (chars/ms) 
len =     12:    342k charAt(i),  342k cbuff[i],  222k new[i],  117k field access.   (chars/ms) 
len =     16:    363k charAt(i),  347k cbuff[i],  275k new[i],  152k field access.   (chars/ms) 
len =     20:    363k charAt(i),  392k cbuff[i],  289k new[i],  180k field access.   (chars/ms) 
len =     24:    375k charAt(i),  428k cbuff[i],  311k new[i],  205k field access.   (chars/ms) 
len =     28:    378k charAt(i),  474k cbuff[i],  341k new[i],  233k field access.   (chars/ms) 
len =     32:    376k charAt(i),  492k cbuff[i],  340k new[i],  251k field access.   (chars/ms) 
len =     64:    374k charAt(i),  551k cbuff[i],  374k new[i],  367k field access.   (chars/ms) 
len =    128:    385k charAt(i),  624k cbuff[i],  415k new[i],  509k field access.   (chars/ms) 
len =    256:    390k charAt(i),  675k cbuff[i],  436k new[i],  619k field access.   (chars/ms) 
len =    512:    394k charAt(i),  703k cbuff[i],  439k new[i],  695k field access.   (chars/ms) 
len =   1024:    395k charAt(i),  718k cbuff[i],  462k new[i],  742k field access.   (chars/ms) 
len =   2048:    396k charAt(i),  725k cbuff[i],  471k new[i],  767k field access.   (chars/ms) 
len =   4096:    396k charAt(i),  727k cbuff[i],  459k new[i],  780k field access.   (chars/ms) 
len =   8192:    397k charAt(i),  712k cbuff[i],  446k new[i],  772k field access.   (chars/ms) 

MODE SERVEUR:

len =      2:     86k charAt(i),   41k cbuff[i],   46k new[i],   80k field access.   (chars/ms) 
len =      4:    571k charAt(i),  250k cbuff[i],   97k new[i],  222k field access.   (chars/ms) 
len =      6:    666k charAt(i),  333k cbuff[i],  125k new[i],  315k field access.   (chars/ms) 
len =      8:    800k charAt(i),  400k cbuff[i],  181k new[i],  380k field access.   (chars/ms) 
len =     12:    800k charAt(i),  521k cbuff[i],  260k new[i],  545k field access.   (chars/ms) 
len =     16:    800k charAt(i),  592k cbuff[i],  296k new[i],  640k field access.   (chars/ms) 
len =     20:    800k charAt(i),  666k cbuff[i],  408k new[i],  800k field access.   (chars/ms) 
len =     24:    800k charAt(i),  705k cbuff[i],  452k new[i],  800k field access.   (chars/ms) 
len =     28:    777k charAt(i),  736k cbuff[i],  368k new[i],  933k field access.   (chars/ms) 
len =     32:    800k charAt(i),  780k cbuff[i],  571k new[i],  969k field access.   (chars/ms) 
len =     64:    800k charAt(i),  901k cbuff[i],  800k new[i],  1306k field access.   (chars/ms) 
len =    128:    1084k charAt(i),  888k cbuff[i],  633k new[i],  1620k field access.   (chars/ms) 
len =    256:    1122k charAt(i),  966k cbuff[i],  729k new[i],  1790k field access.   (chars/ms) 
len =    512:    1163k charAt(i),  1007k cbuff[i],  676k new[i],  1910k field access.   (chars/ms) 
len =   1024:    1179k charAt(i),  1027k cbuff[i],  698k new[i],  1954k field access.   (chars/ms) 
len =   2048:    1184k charAt(i),  1043k cbuff[i],  732k new[i],  2007k field access.   (chars/ms) 
len =   4096:    1188k charAt(i),  1049k cbuff[i],  742k new[i],  2031k field access.   (chars/ms) 
len =   8192:    1157k charAt(i),  1032k cbuff[i],  723k new[i],  2048k field access.   (chars/ms) 

CONCLUSION:

Comme vous pouvez le voir, le mode serveur est beaucoup plus rapide.

7voto

adarshr Points 25912

Le premier à utiliser str.charAt devrait être plus rapide.

Si vous creusez l'intérieur du code source de l' String classe, nous pouvons voir qu' charAt est mis en œuvre comme suit:

public char charAt(int index) {
    if ((index < 0) || (index >= count)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index + offset];
}

Ici, tout ce qu'il fait est l'indice d'un tableau et renvoie la valeur.

Maintenant, si nous voyons la mise en œuvre de l' toCharArray,, nous allons trouver le ci-dessous:

public char[] toCharArray() {
    char result[] = new char[count];
    getChars(0, count, result, 0);
    return result;
}

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(srcBegin);
    }
    if (srcEnd > count) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    System.arraycopy(value, offset + srcBegin, dst, dstBegin,
         srcEnd - srcBegin);
}

Comme vous le voyez, il est en train de faire un System.arraycopy qui va certainement être un peu plus lent que ne pas le faire.

2voto

Piotr Gwiazda Points 5208

Ressemble à aucune, est plus rapide ou plus lent

    public static void main(String arguments[]) {


        //Build a long string
        StringBuilder sb = new StringBuilder();
        for(int j = 0; j < 10000; j++) {
            sb.append("a really, really long string");
        }
        String str = sb.toString();
        for (int testscount = 0; testscount < 10; testscount ++) {


            //Test 1
            long start = System.currentTimeMillis();
            for(int c = 0; c < 10000000; c++) {
                for (int i = 0, n = str.length(); i < n; i++) {
                    char chr = str.charAt(i);
                    doSomethingWithChar(chr);//To trick JIT optimistaion
                }
            }

            System.out.println("1: " + (System.currentTimeMillis() - start));

            //Test 2
            start = System.currentTimeMillis();
            char[] chars = str.toCharArray();
            for(int c = 0; c < 10000000; c++) {
                for (int i = 0, n = chars.length; i < n; i++) {
                    char chr = chars[i];
                    doSomethingWithChar(chr);//To trick JIT optimistaion
                }
            }
            System.out.println("2: " + (System.currentTimeMillis() - start));
            System.out.println();
        }


    }


    public static void doSomethingWithChar(char chr) {
        int newInt = chr << 2;
    }

Pendant de longues chaînes je vais vous choisi le premier. Pourquoi copier autour de longues chaînes? Documentations dit:

public char[] toCharArray() Convertit la chaîne de caractères à un nouveau tableau de caractères.

Retourne: un nouvellement allouée tableau de caractères dont la longueur est la longueur de cette chaîne et dont le contenu est initialisé à contenir la séquence de caractères représenté par cette chaîne.

//Edit 1

J'ai changé le test de tromper JIT optimisation.

//Edit 2

Répétez le test 10 fois pour permettre à la JVM se réchauffer.

//Edit 3

Conclusions:

Tout d'abord str.toCharArray(); des copies de toute la chaîne de caractères en mémoire. Il peut être la consommation de mémoire pour de longues cordes. Méthode String.charAt( ) recherche de char en char tableau à l'intérieur de la classe String vérification de l'index avant de. Il ressemble assez court pour les Chaînes de la première méthode (c - chatAt méthode) est un peu plus lent en raison de cet indice case. Mais si la Chaîne est assez longue, la copie de l'ensemble char tableau devient plus lent, et la première méthode est plus rapide. Plus la chaîne est, le plus lent, toCharArray effectue. Essayez de changer de limite en for(int j = 0; j < 10000; j++) boucle pour le voir. Si nous laissons la JVM réchauffer le code s'exécute plus rapidement, mais les proportions sont les mêmes.

Après tout, c'est juste de la micro-optimisation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X