9
votes

Java Expression régulière offre un avantage de performance?

En Java, lorsque nous essayons de faire des motifs correspondant à une expression régulière. par exemple. Prenez une chaîne d'entrée et utilisez une expression régulière pour savoir s'il est numérique. Sinon, lancez une exception. Dans ce cas, je comprends, à l'aide de REGEX rend le code moins verbeux que si nous devions prendre chaque caractère de la chaîne, vérifiez s'il s'agit d'un nombre et de lancer une exception.

Mais j'étais sous l'hypothèse que Regex rend également le processus plus efficace. Est-ce vrai? Je ne trouve aucune preuve sur ce point. Comment la regex fait-elle le match derrière les scènes? N'est-ce pas aussi itération de la chaîne et de vérifier chaque personnage un par un?


1 commentaires

Un moyen simple de savoir: exécutez des options et du temps. Le processus est lié à la CPU, de sorte que la durée avec vous dit ce qui est plus efficace. Notez que vous pouvez rendre plus efficace plus efficace à la réutilisation du motif compilé, plutôt que d'utiliser string.matches () , qui compilait la réégalité de chaque appel.


8 Réponses :


3
votes

Je n'ai pas encore de réponse technique, mais je pourrais écrire du code et voir. Je ne pense pas que les expressions régulières seraient la voie à suivre pour convertir une chaîne en un nombre. Dans de nombreux cas, ils peuvent être plus efficaces, mais si c'est écrit mal, cela sera lent.

Puis-je vous demander cependant pourquoi n'utilisez-vous pas simplement: INTEGER.PARSINT ("124") ? Cela lancera une exception numérique. Devrait être capable de le gérer, et il laisse la détection d'un nombre à Java Core.


2 commentaires

+1. Bien que pour une série de chiffres de manière significative, même longtemps.parelong lancerait une exception numérique. Je ne sais pas comment les numéros d'Apache Commons fonctionnent exactement, mais il y a une méthode appelée isDigits (String Str) qui peut vous dire si une chaîne est un nombre valide (au moins en fonction de Java). Commons.apache.org/lang/ API-2.6 / ORG / Apache / Commons / Lang / Math / ...


Résultats intéressants avec votre regex ci-dessous. Je serais également intéressé de voir ce que les résultats sont pour une non-correspondance et de tout inverser. Dépend de la façon dont Java gère Regex.



0
votes

Eh bien, il est difficile de dire à coup sûr, mais en général, les expressions régulières sont moins susceptibles d'être plus efficaces par rapport à la vérification explicite du caractère. Re est un automatha final de l'état, de sorte qu'il y a des frais généraux sur le bâtiment d'automate et le maintien. Dans ma pratique, le code explicite est toujours plus rapide (et donc plus efficace) que les expressions régulières.

Mais voici le dilemme. Les expressions régulières sont presque toujours plus efficaces du point de vue temporel et plus lisible lorsqu'ils sont utilisés correctement . Et voici un autre dilemme. Je vois tellement rarement l'utilisation correcte d'expressions régulières ...

Dans votre scénario, je suggère d'utiliser la bibliothèque de guitages: xxx


0 commentaires

0
votes

À la fin, il est en effet itérant sur la chaîne et vérifiant chaque personnage en essayant de trouver une correspondance pour le motif fourni. De plus, il utilise une recul (s'il y a plusieurs façons qui pourraient éventuellement correspondre, le moteur les essayera tous), ce qui pourrait entraîner une très mauvaise performance pour certains cas inhabituels (peu probable que vous rencontriez cela, mais théoriquement possible). Dans le pire des cas, les performances du moteur d'expression régulier Java sont O (2 n ), où n est la longueur de la chaîne d'entrée.

Il existe des algorithmes pour une correspondance de modèles beaucoup plus rapide fournissant des performances O (n), mais avec moins de caractéristiques comparant les expressions régulières Java.

ici est un article qui discute de cette question dans les détails.

Mais dans la plupart des cas, le moteur d'expression régulier ne sera pas le goulot d'étranglement de la performance de votre application. Il est assez rapide, donc généralement ne vous inquiétez pas à ce sujet à moins que votre profileur ne le pointe. Et il fournit une description déclarative de l'algorithme qui est très utile car la mise en œuvre d'algorithme d'itération presque toujours sera beaucoup plus verbeuse et beaucoup moins lisible.


0 commentaires

4
votes

Juste pour le plaisir, j'ai géré ce micro de référence. Les résultats de la dernière exécution (c.-à-d. Post JVM Warm Up / Jit) sont ci-dessous (les résultats sont assez cohérents d'une course à un autre quand même): XXX PRE>

En d'autres termes, les caractères sont très efficaces, Integer.parseint est aussi efficace que CHAR si la chaîne est un nombre, mais tergue terriblement si la chaîne n'est pas un nombre. Regex est entre les deux. P>

conclusion forte> p>

Si vous analysez une chaîne dans un numéro et que vous vous attendez à ce que la chaîne soit un nombre en général, en utilisant Entreger. Paysint est la meilleure solution (efficace et lisible). La pénalité que vous obtenez lorsque la chaîne n'est pas un nombre ne doit pas être faible si ce n'est pas trop fréquent. P>

PS: Mon regex n'est peut-être pas optimal, n'hésitez pas à commenter. P>

public class TestNumber {

    private final static List<String> numbers = new ArrayList<>();
    private final static List<String> words = new ArrayList<>();

    public static void main(String args[]) {
        long start, end;
        Random random = new Random();

        for (int i = 0; i < 1000000; i++) {
            numbers.add(String.valueOf(i));
            words.add(String.valueOf(i) + "x");
        }

        for (int i = 0; i < 5; i++) {
            start = System.nanoTime();
            regex(numbers);
            System.out.println("regex with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(numbers);
            System.out.println("chars with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(numbers);
            System.out.println("exceptions with numbers " + (System.nanoTime() - start) / 1000000);

            start = System.nanoTime();
            regex(words);
            System.out.println("regex with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(words);
            System.out.println("chars with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(words);
            System.out.println("exceptions with words " + (System.nanoTime() - start) / 1000000);
        }
    }

    private static int regex(List<String> list) {
        int sum = 0;
        Pattern p = Pattern.compile("[0-9]+");
        for (String s : list) {
            sum += (p.matcher(s).matches() ? 1 : 0);
        }
        return sum;
    }

    private static int chars(List<String> list) {
        int sum = 0;

        for (String s : list) {
            boolean isNumber = true;
            for (char c : s.toCharArray()) {
                if (c < '0' || c > '9') {
                    isNumber = false;
                    break;
                }
            }
            if (isNumber) {
                sum++;
            }
        }
        return sum;
    }

    private static int exception(List<String> list) {
        int sum = 0;

        for (String s : list) {
            try {
                Integer.parseInt(s);
                sum++;
            } catch (NumberFormatException e) {
            }
        }
        return sum;
    }
}


3 commentaires

Lancer et attraper une exception est généralement une opération assez coûteuse. Si vous êtes sûr que le format est strictement des chiffres sans regroupement ni séparateur décimal, l'approche de charme est probablement la plus rapide que vous puissiez atteindre, bien que j'utilise le caractère.Indigit plutôt que si vous avez déjà été au-dessus. Si vous avez besoin d'une prise en charge plus robuste pour le regroupement et les séparateurs décimaux, vous risquez de faire mieux avec une regex ou un objet numerformat.


"Lancer et attraper une exception est typiquement une opération assez coûteuse" bien oui, mais le point est que lorsque l'entrée est un nombre, Parsint est aussi rapide et traite des choses que l'on pourrait oublier (signe etc.). C'est donc plus robuste et aussi rapide: aucune raison de ne pas l'utiliser à moins que vous sachiez que vous obtiendrez de nombreuses intrants qui lanceront une exception.


Je suis d'accord, que si vous ne le faites pas pour un grand nombre d'appels, Parsint est correct, même si je ne suis pas sûr que Parsint gère le regroupement des séparateurs etc.



0
votes

Pour répondre à votre question spécifiquement:

Pourquoi ne pas appliquer une correspondance de modèle de regex sur un texte complexe, puis tenter d'écrire vous-même le même code correspondant.

Voir ce qui est plus rapide.

Réponse: la regex.


0 commentaires

1
votes

À propos de RegEx derrière les scènes ...

a machine à états finis (FSM) équivaut à une expression régulière. FSM est une machine capable de reconnaître une langue (dans vos numéros de cas). FSM dispose d'un alphabet, d'un État, d'un état initial, des États N-finaux et de transition d'un État à un autre. La chaîne doit être contenir dans l'alphabet (ASCII par exemple). Le FSM commence à l'état initial. Lorsque vous entrez une chaîne IT Process Processus PAR CHARME Passez de l'état à l'état en fonction d'une fonction (état, char) => état. Lorsqu'il atteint un état final, vous savez si votre chaîne est numérique ou non.

Pour plus, voir FSM et voir automate-basé_programming


0 commentaires

1
votes

Je ne vois pas comment cela pourrait être plus simple ou plus facile à lire que:

integer.parseint ()

ou

double.Parsedouble ()

Ils font exactement ce que vous décrivez, y compris lancer une exception pour une entrée non valide.

En ce qui concerne la performance: je m'attendrais à une regex moins efficace que ce qui précède.


0 commentaires

1
votes

juste mes 5 cents :) En général, les expressions ordinaires, la langue n'est pas destinée à analyser uniquement des entiers ou des chaînes, son outil tout à fait puissant permettant de reconnaître toute «expression régulière». Cela me rappelle ma période de université (souvenez-vous de la théorie des automates? :), mais voici le lien que décrit ce que la langue régulière est vraiment

Maintenant, car il construit des FSMS, il introduit des frais généraux, alors peut-être pour integer.parseint Le moteur d'expression régulier n'est pas une bonne substitution, de plus, Java a introduit l'API plus spécifique. Cependant, les expressions régulières ont un avantage lorsque vous travaillez avec des expressions plus complexes et lorsque nous en avons beaucoup.

L'expression régulière doit être utilisée judicieusement. Le motif doit toujours être compilé (sinon, il ne peut pas être réutilisé efficacement, car la compilation du motif chaque fois videra les performances)

Je suggérerais d'exécuter le test sur une entrée plus complexe et de voir ce qui se passe.


0 commentaires