64 votes

Java : Enum et Int.

Lorsque l'on utilise des drapeaux en Java, j'ai vu deux approches principales. L'une utilise des valeurs int et une ligne d'instructions if-else. L'autre consiste à utiliser des enums et des instructions de changement de cas.

Je me demandais s'il y avait une différence en termes d'utilisation de la mémoire et de vitesse entre l'utilisation des enums et des ints pour les drapeaux ?

3voto

Sergey Alaev Points 1428

Oui, il y a une différence. Dans le java 64 bits moderne, les valeurs d'Enum sont essentiellement des pointeurs vers des objets et elles prennent 64 bits (opérations non compressées) ou utilisent du CPU supplémentaire (opérations compressées).

Mon test a montré une dégradation des performances d'environ 10% pour les enums (1.8u25, AMD FX-4100) : 13k ns vs 14k ns

Source du test ci-dessous :

public class Test {

    public static enum Enum {
        ONE, TWO, THREE
    }

    static class CEnum {
        public Enum e;
    }

    static class CInt {
        public int i;
    }

    public static void main(String[] args) {
        CEnum[] enums = new CEnum[8192];
        CInt[] ints = new CInt[8192];

        for (int i = 0 ; i < 8192 ; i++) {
            enums[i] = new CEnum();
            ints[i] = new CInt();
            ints[i].i = 1 + (i % 3);
            if (i % 3 == 0) {
                enums[i].e = Enum.ONE;
            } else if (i % 3 == 1) {
                enums[i].e = Enum.TWO;
            } else {
                enums[i].e = Enum.THREE;
            }
        }
        int k=0; //calculate something to prevent tests to be optimized out

        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);

        System.out.println();

        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);

        System.out.println(k);

    }

    private static int test2(CInt[] ints) {
        long t;
        int k = 0;
        for (int i = 0 ; i < 1000 ; i++) {
            k+=test(ints);
        }

        t = System.nanoTime();
        k+=test(ints);
        System.out.println((System.nanoTime() - t)/100 + "ns");
        return k;
    }

    private static int test1(CEnum[] enums) {
        int k = 0;
        for (int i = 0 ; i < 1000 ; i++) {
            k+=test(enums);
        }

        long t = System.nanoTime();
        k+=test(enums);
        System.out.println((System.nanoTime() - t)/100 + "ns");
        return k;
    }

    private static int test(CEnum[] enums) {
        int i1 = 0;
        int i2 = 0;
        int i3 = 0;

        for (int j = 100 ; j != 0 ; --j)
        for (int i = 0 ; i < 8192 ; i++) {
            CEnum c = enums[i];
            if (c.e == Enum.ONE) {
                i1++;
            } else if (c.e == Enum.TWO) {
                i2++;
            } else {
                i3++;
            }
        }

        return i1 + i2*2 + i3*3;
    }

    private static int test(CInt[] enums) {
        int i1 = 0;
        int i2 = 0;
        int i3 = 0;

        for (int j = 100 ; j != 0 ; --j)
        for (int i = 0 ; i < 8192 ; i++) {
            CInt c = enums[i];
            if (c.i == 1) {
                i1++;
            } else if (c.i == 2) {
                i2++;
            } else {
                i3++;
            }
        }

        return i1 + i2*2 + i3*3;
    }
}

2voto

Joe Points 2191

Réponse à votre question : Non, après un temps négligeable pour charger la classe Enum, les performances sont les mêmes.

Comme d'autres l'ont dit, les deux types peuvent être utilisés dans des instructions switch ou if else. De plus, comme d'autres l'ont dit, vous devriez préférer les Enums aux int flags, car ils ont été conçus pour remplacer ce modèle et ils offrent une sécurité supplémentaire.

CEPENDANT, il existe un meilleur modèle que vous pouvez considérer. Fournir la valeur que votre instruction switch/if était censée produire comme propriété.

Regardez ce lien : http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html Remarquez le modèle fourni pour donner les masses et les rayons des planètes. En fournissant la propriété de cette manière, vous vous assurez de ne pas oublier de couvrir un cas si vous ajoutez un enum.

2voto

user3708686 Points 81

J'aime utiliser les Enums quand c'est possible mais j'ai eu une situation où je devais calculer des millions de décalages de fichiers pour différents types de fichiers que j'avais définis dans un enum et j'ai dû exécuter une instruction switch des dizaines de millions de fois pour calculer le décalage sur la base du type d'enum. J'ai effectué le test suivant :

import java.util.Random;

classe publique switchTest { public enum MyEnum { Valeur1, Valeur2, Valeur3, Valeur4, Valeur5 } ;

public static void main(String[] args)
{
    final String s1 = "Value1";
    final String s2 = "Value2";
    final String s3 = "Value3";
    final String s4 = "Value4";
    final String s5 = "Value5";

    String[] strings = new String[]
    {
        s1, s2, s3, s4, s5
    };

    Random r = new Random();

    long l = 0;

    long t1 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        String s = strings[r.nextInt(5)];

        switch(s)
        {
            case s1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case s2:
                l = r.nextInt(10);
                break;
            case s3:
                l = r.nextInt(15);
                break;
            case s4:
                l = r.nextInt(20);
                break;
            case s5:
                l = r.nextInt(25);
                break;
        }
    }

    long t2 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        MyEnum e = MyEnum.values()[r.nextInt(5)];

        switch(e)
        {
            case Value1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case Value2:
                l = r.nextInt(10);
                break;
            case Value3:
                l = r.nextInt(15);
                break;
            case Value4:
                l = r.nextInt(20);
                break;
            case Value5:
                l = r.nextInt(25);
                break;
        }
    }

    long t3 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        int xx = r.nextInt(5);

        switch(xx)
        {
            case 1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case 2:
                l = r.nextInt(10);
                break;
            case 3:
                l = r.nextInt(15);
                break;
            case 4:
                l = r.nextInt(20);
                break;
            case 5:
                l = r.nextInt(25);
                break;
        }
    }

    long t4 = System.currentTimeMillis();

    System.out.println("strings:" + (t2 - t1));
    System.out.println("enums  :" + (t3 - t2));
    System.out.println("ints   :" + (t4 - t3));
}

}

et j'ai obtenu les résultats suivants :

cordes:442

enums :455

ints :362

J'ai donc décidé que les enums étaient suffisamment efficaces pour moi. Lorsque j'ai réduit le nombre de boucles de 10M à 1M, les chaînes et les enums prenaient environ deux fois plus de temps que les int, ce qui indique qu'il y avait un certain surcoût à utiliser les chaînes et les enums pour la première fois par rapport aux ints.

0voto

John Points 1

Même si cette question est ancienne, j'aimerais souligner ce que vous ne pouvez pas faire avec les ints.

public interface AttributeProcessor {
    public void process(AttributeLexer attributeLexer, char c);
}

public enum ParseArrayEnd implements AttributeProcessor {
    State1{
        public void process(AttributeLexer attributeLexer, char c) {
            .....}},
    State2{
        public void process(AttributeLexer attributeLexer, char c) {
            .....}}
}

Ce que vous pouvez faire, c'est établir une carte de la valeur attendue en tant que clé, et de l'enum en tant que valeur,

Map<String, AttributeProcessor> map 
map.getOrDefault(key, ParseArrayEnd.State1).process(this, c);

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