test - sort with enum java




Comparer les membres de Java enum:== ou equals()? (10)

Je sais que les enums Java sont compilés en classes avec des constructeurs privés et un groupe de membres statiques publics. Lorsque je compare deux membres d'une énumération donnée, j'ai toujours utilisé .equals() , par exemple

public useEnums(SomeEnum a)
{
    if(a.equals(SomeEnum.SOME_ENUM_VALUE))
    {
        ...
    }
    ...
}

Cependant, je suis juste tombé sur du code qui utilise l'opérateur égal == au lieu de .equals ():

public useEnums2(SomeEnum a)
{
    if(a == SomeEnum.SOME_ENUM_VALUE)
    {
        ...
    }
    ...
}

Quel opérateur est celui que je devrais utiliser?


Peut == être utilisé sur enum ?

Oui: les enums ont des contrôles d'instance serrés qui vous permettent d'utiliser == pour comparer des instances. Voici la garantie fournie par la spécification de la langue (je souligne par moi):

JLS 8.9 Enums

Un type enum n'a pas d'instances autres que celles définies par ses constantes enum.

C'est une erreur de compilation pour tenter d'instancier explicitement un type enum. La méthode final clone dans Enum garantit que les constantes enum ne peuvent jamais être clonées et le traitement spécial du mécanisme de sérialisation garantit que les instances en double ne sont jamais créées suite à la désérialisation. L'instanciation réfléchissante des types enum est interdite. Ensemble, ces quatre choses assurent qu'aucune instance d'un type enum n'existe au-delà de celles définies par les constantes enum .

Comme il n'y a qu'une instance de chaque constante enum , il est permis d'utiliser l'opérateur == à la place de la méthode equals lors de la comparaison de deux références d'objet si l'on sait qu'au moins l'une d'entre elles se réfère à une constante enum . (La méthode equals dans Enum est une méthode final qui appelle simplement super.equals sur son argument et renvoie le résultat, effectuant ainsi une comparaison d'identité.)

Cette garantie est assez forte que Josh Bloch recommande, que si vous insistez sur l'utilisation du pattern singleton, la meilleure façon de l'implémenter est d'utiliser une enum mono-élément (voir: Effective Java 2nd Edition, Item 3: Appliquer la propriété singleton avec un constructeur privé ou un type enum , également Thread safety dans Singleton )

Quelles sont les différences entre == et equals ?

Pour rappel, il faut dire que généralement, == n'est pas une alternative viable aux equals . Quand il est, cependant (comme avec enum ), il y a deux différences importantes à considérer:

== ne lance jamais NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== est soumis à une vérification de compatibilité de type à la compilation

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

Devrait == être utilisé le cas échéant?

Bloch mentionne spécifiquement que les classes immuables qui ont un contrôle approprié sur leurs instances peuvent garantir à leurs clients que == est utilisable. enum est spécifiquement mentionné pour illustrer.

Point 1: Considérer les méthodes d'usine statiques à la place des constructeurs

[...] cela permet à une classe immuable de garantir qu'il n'existe pas deux instances égales: a.equals(b) si et seulement si a==b . Si une classe fait cette garantie, alors ses clients peuvent utiliser l'opérateur == au lieu de la méthode equals(Object) , ce qui peut améliorer les performances. Les types Enum fournissent cette garantie.

Pour résumer, les arguments pour utiliser == sur enum sont:

  • Ça marche.
  • C'est plus rapide.
  • C'est plus sûr à l'exécution.
  • C'est plus sûr à la compilation.

Comme d'autres l'ont dit, à la fois == et .equals() fonctionnent dans la plupart des cas. La certitude du temps de compilation que vous ne comparez pas complètement différents types d'objets que d'autres ont signalés est valide et bénéfique, cependant le type particulier de bogue de comparaison d'objets de deux types de temps de compilation différents sera également trouvé par FindBugs (et probablement par Inspections de temps de compilation Eclipse / IntelliJ), de sorte que le compilateur Java trouve que cela n'ajoute pas beaucoup de sécurité supplémentaire.

Toutefois:

  1. Le fait que == ne jette jamais NPE dans mon esprit est un désavantage de == . Il ne devrait presque jamais être nécessaire que les types enum soient null , car tout état supplémentaire que vous voudrez peut-être exprimer via null peut simplement être ajouté à l' enum tant enum supplémentaire. Si elle est de manière inattendue null , je préférerais avoir un NPE que == évaluant silencieusement à false. Par conséquent, je ne suis pas d'accord avec le fait qu'il est plus sûr à l'heure d'exécution ; il est préférable de prendre l'habitude de ne jamais laisser les valeurs enum être @Nullable .
  2. L'argument selon lequel == est plus rapide est également faux. Dans la plupart des cas, vous appelez .equals() sur une variable dont le type de compilation est la classe enum, et dans ces cas le compilateur peut savoir que c'est la même chose que == (car une méthode enum equals() enum peut pas être remplacé) et peut optimiser l'appel de la fonction. Je ne suis pas sûr si le compilateur le fait actuellement, mais si ce n'est pas le cas, et s'avère être un problème de performance dans l'ensemble de Java, alors je préfère réparer le compilateur que 100 000 programmeurs Java changent leur style de programmation les caractéristiques de performance d'une version particulière du compilateur.
  3. enums sont des objets. Pour tous les autres types d'objets, la comparaison standard est .equals() , pas == . Je pense qu'il est dangereux de faire une exception pour les enums parce que vous pourriez finir par comparer accidentellement les objets avec == au lieu de equals() , surtout si vous refactorisez une enum dans une classe non-enum. Dans le cas d'un tel refactoring, le point It works from above est faux. Pour vous convaincre qu'une utilisation de == est correcte, vous devez vérifier si la valeur en question est une enum ou une primitive; Si c'était une classe non enum , ce serait faux mais facile à manquer car le code compilerait toujours. Le seul cas où une utilisation de .equals() serait erronée est si les valeurs en question étaient des primitives; dans ce cas, le code ne compile pas, c'est donc beaucoup plus difficile à manquer. Par conséquent, .equals() est beaucoup plus facile à identifier comme correct, et est plus sûr contre les refactorings futurs.

Je pense en fait que le langage Java aurait dû définir == sur les objets pour appeler .equals () sur la valeur de gauche, et introduire un opérateur séparé pour l'identité de l'objet, mais ce n'est pas comme cela que Java a été défini.

En résumé, je pense toujours que les arguments sont en faveur de l'utilisation de .equals() pour les types enum .


En cas d'énumération les deux sont corrects et corrects !!


Enum au milieu est un ensemble d'entiers constants. "==" est tout aussi valide et correct que si vous compariez deux nombres entiers.


Je veux compléter les polygenelubricants répondre:

Personnellement, je préfère égal (). Mais il lac le contrôle de compatibilité de type. Ce que je pense est une limitation importante.

Pour vérifier la compatibilité des types au moment de la compilation, déclarez et utilisez une fonction personnalisée dans votre énumération.

public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable

Avec ceci, vous avez tout l'avantage des deux solutions: protection NPE, code facile à lire et vérification de compatibilité de type au moment de la compilation.

Je recommande également d'ajouter une valeur UNDEFINED pour enum.


Je voudrais souligner explicitement cette différence spécifique entre l'opérateur == et la méthode equals() :

La méthode equals() est destinée à vérifier si le contenu de l'objet (des objets) de la (des) variable (s) de référence concernée (s) est le même.

L'opérateur == vérifie si la (les) variable (s) de référence impliquée (s) se réfère (nt) au même objet .

Il appartient à la classe d'implémentation de fournir cette différenciation selon les besoins de l'application.

Sinon, le comportement par défaut sera celui fourni par la classe Object (en Java), comme expliqué dans http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals(java.lang.Object) :

La méthode equals de la classe Object implémente la relation d'équivalence la plus discriminante possible sur les objets; c'est-à-dire, pour toute valeur de référence non nulle x et y , cette méthode renvoie true si et seulement si x et y réfèrent au même objet ( x == y a la valeur true ).


La raison pour laquelle les énumérations fonctionnent facilement avec == est que chaque instance définie est également un singleton. La comparaison d'identité utilisant == fonctionnera donc toujours.

Mais utiliser == parce que cela fonctionne avec enums signifie que tout votre code est étroitement lié à l'utilisation de cette énumération.

Par exemple: Enums peut implémenter une interface. Supposons que vous utilisez actuellement une énumération qui implémente Interface1. Si plus tard, quelqu'un le change ou introduit une nouvelle classe Impl1 en tant qu'implémentation de la même interface. Ensuite, si vous commencez à utiliser des instances d'Impl1, vous aurez beaucoup de code à modifier et à tester en raison de l'utilisation précédente de ==.

Par conséquent, il est préférable de suivre ce qui est considéré comme une bonne pratique à moins qu'il n'y ait un gain justifiable.


Les énumérations sont des classes qui renvoient une instance (comme des singletons) pour chaque constante d'énumération déclarée par public static final field (immuable) afin que l'opérateur == puisse être utilisé pour vérifier leur égalité plutôt que d'utiliser la méthode equals()


Utiliser autre chose que == pour comparer les constantes enum est un non-sens. C'est comme comparer class objets de class avec des equals - ne le faites pas!

Cependant, il y avait un bug méchant (BugId 6277781 ) dans Sun JDK 6u10 et plus tôt qui pourrait être intéressant pour des raisons historiques. Ce bug empêchait l'utilisation correcte de == sur des énumérations désérialisées, bien que ce soit sans doute un peu un cas d'angle.


Voici un test de synchronisation brut pour comparer les deux:

import java.util.Date;

public class EnumCompareSpeedTest {

    static enum TestEnum {ONE, TWO, THREE }

    public static void main(String [] args) {

        Date before = new Date();
        int c = 0;

        for(int y=0;y<5;++y) {
            for(int x=0;x<Integer.MAX_VALUE;++x) {
                if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
                if(TestEnum.ONE == TestEnum.TWO){++c;}              
            }
        }

        System.out.println(new Date().getTime() - before.getTime());
    }   

}

Commentez les FI une à la fois. Voici les deux compare d'en haut dans le byte-code démonté:

 21  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27  invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30  ifeq 36

 36  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42  if_acmpne 48

Le premier (égal à) effectue un appel virtuel et teste le retour booléen de la pile. La seconde (==) compare les adresses d'objets directement à partir de la pile. Dans le premier cas, il y a plus d'activité.

J'ai effectué ce test plusieurs fois avec les deux FI une à la fois. Le "==" est toujours un peu plus rapide.







enums