web-dev-qa-db-fra.com

Enum.values ​​() vs EnumSet.allOf (). Lequel est le plus préférable?

J'ai regardé sous le capot pour EnumSet.allOf et cela a l'air très efficace, surtout pour les enums avec moins de 64 valeurs.

Fondamentalement, tous les ensembles partagent le tableau unique de toutes les valeurs enum possibles et le seul autre élément d'information est un masque de bits qui, dans le cas de allOf, est défini dans un échange.

En revanche, Enum.values ​​() semble être un peu de magie noire. De plus, il retourne un tableau et non une collection. Par conséquent, dans de nombreux cas, il doit être décoré avec Arrays.asList () pour pouvoir être utilisé dans tout lieu qui attend une collection.

Donc, EnumSet.allOf devrait-il être plus préférable à Enum.values?

Plus précisément, quelle forme d'itérateur for devrait être utilisée:

for ( final MyEnum val: MyEnum.values( ) );

ou

for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
55

Parce que je n’ai pas reçu de réponse à ma question sur celle qui est plus efficace, j’ai décidé de faire mes propres tests.

J'ai testé l'itération sur values(), Arrays.asList( values() ) et EnumSet.allOf( ). J'ai répété ces tests 10 000 000 fois pour différentes tailles d'enum. Voici les résultats du test:

oneValueEnum_testValues         1.328
oneValueEnum_testList           1.687
oneValueEnum_testEnumSet        0.578

TwoValuesEnum_testValues        1.360
TwoValuesEnum_testList          1.906
TwoValuesEnum_testEnumSet       0.797

ThreeValuesEnum_testValues      1.343
ThreeValuesEnum_testList        2.141
ThreeValuesEnum_testEnumSet     1.000

FourValuesEnum_testValues       1.375
FourValuesEnum_testList         2.359
FourValuesEnum_testEnumSet      1.219

TenValuesEnum_testValues        1.453
TenValuesEnum_testList          3.531
TenValuesEnum_testEnumSet       2.485

TwentyValuesEnum_testValues     1.656
TwentyValuesEnum_testList       5.578
TwentyValuesEnum_testEnumSet    4.750

FortyValuesEnum_testValues      2.016
FortyValuesEnum_testList        9.703
FortyValuesEnum_testEnumSet     9.266

Ce sont les résultats des tests exécutés à partir de la ligne de commande. Lorsque j’ai exécuté ces tests à partir d’Eclipse, j’ai reçu un soutien écrasant pour testValues. Fondamentalement, il était plus petit que EnumSet, même pour de petits enums. Je pense que le gain de performance provient de l'optimisation de l'itérateur de tableau en boucle for ( val : array ).

D'un autre côté, dès que vous avez besoin d'une Java.util.Collection à distribuer, Arrays.asList( ) perd le contrôle vers EnumSet.allOf, en particulier pour les petites énumérations, qui, à mon avis, constitueront une majorité dans un code donné.

Donc, je dirais que vous devriez utiliser

for ( final MyEnum val: MyEnum.values( ) )

mais

Iterables.filter(
    EnumSet.allOf( MyEnum.class ),
    new Predicate< MyEnum >( ) {...}
)

Et n'utilisez que Arrays.asList( MyEnum.values( ) )Java.util.List est absolument requis.

86

Vous devriez utiliser l'approche qui est la plus simple et la plus claire pour vous. La performance ne devrait pas être prise en compte dans la plupart des situations.

IMHO: aucune option ne fonctionne très bien car ils créent tous les deux des objets. Un dans le premier cas et trois dans le second. Vous pouvez construire une constante contenant toutes les valeurs pour des raisons de performances.

12
Peter Lawrey

Il y a aussi Class.getEnumConstants()

sous le capot, ils appellent tous de toute façon des méthodes values() de types enum, par réflexion .

7
irreputable

La méthode values() est plus claire et plus performante si vous souhaitez simplement parcourir toutes les valeurs enum possibles. Les valeurs sont mises en cache par la classe (voir Class.getEnumConstants())

Si vous avez besoin d’un sous-ensemble de valeurs, vous devez utiliser une variable EnumSet. Commencez par allOf() ou noneOf() et ajoutez ou supprimez des valeurs ou utilisez simplement of() selon vos besoins.

4
Arne Burmeister

Ce n’est pas que j’ai parcouru toute l’implémentation, mais il me semble que EnumSet.allOf () utilise essentiellement la même infrastructure que .values ​​(). Je suppose donc qu'EnumSet.allOf () nécessite quelques étapes supplémentaires (probablement négligeables) (voir http://bugs.Sun.com/bugdatabase/view_bug.do?bug_id=6276988 ).

Il me semble clair que l'utilisation prévue de foreach est for(MyEnum val : MyEnum.values()) pourquoi le faire différemment? Vous ne ferez que confondre le programmeur de maintenance.

Je veux dire, si vous avez besoin d'une collection, vous devriez en avoir une. Si vous voulez utiliser un foreach, les tableaux sont assez bons. Je préférerais même des tableaux si on appuie dessus! Pourquoi envelopper quoi que ce soit avec quoi que ce soit, si ce que vous avez (tableau) est assez bon? Les choses simples sont normalement plus rapides.

En tout cas, Peter Lawrey a raison. Ne vous inquiétez pas des performances de ceci .. C'est assez rapide, et il y a des chances qu'il y ait des millions d'autres goulots d'étranglement qui rendent cette toute petite différence de performances théoriques totalement dénuée de pertinence (Ne voyez pas son point "Création d'objet" cependant. exemple semble être 100% OK).

2
Enno Shioji

EnumSet n'est pas construit avec l'intention de parcourir ses valeurs. Plutôt, il est implémenté avec l’idée qu’il représente un BitMap ou un BitMask de manière efficace (ou raisonnablement efficace). Le javadoc sur EnumSet indique également:

Les ensembles d'énumération sont représentés de manière interne en tant que vecteurs de bits. Cette représentation est extrêmement compacte et efficace. Les performances spatio-temporelles de cette classe doivent être suffisantes pour permettre son utilisation en tant qu’alternative de type haute qualité et de haute qualité aux "indicateurs de bits" classiques basés sur int. Même les opérations en bloc (telles que containsAll et rétentionAll) devraient s'exécuter très rapidement si leur argument est également un ensemble énuméré.

Comme un seul bit peut représenter une certaine valeur Enum, il est également implémenté en tant que Set et non en tant que List.

Maintenant, il est probablement aussi vrai que vous pouvez accomplir la même chose, et plus rapidement, en utilisant des masques de bits de style C (x ^ 2). Cependant, il offre un style de codage plus intuitif et une utilisation sans risque en utilisant des enum taille de ce que int ou long peut contenir.

En tant que tel, vous pouvez vérifier que tous les bits sont définis comme suit:

public class App {
  enum T {A,B}
  public static void main(String [] args) {
    EnumSet<T> t = EnumSet.of(T.A);
    t.containsAll(EnumSet.allOf(T.class));
  }
}
0
YoYo