web-dev-qa-db-fra.com

Java 8 façons de travailler avec un enum

Je me demande quelle est la meilleure façon de travailler avec Java 8 avec toutes les valeurs d'une énumération. Spécifiquement lorsque vous devez obtenir toutes les valeurs et les ajouter à quelque part, par exemple, en supposant que nous ayons l'énumération suivante:

public enum Letter {
 A, B, C, D;
}

Je pourrais bien sûr faire ce qui suit:

for (Letter l : Letter.values()) {
    foo(l);
}

Mais, je pourrais aussi ajouter la méthode suivante à la définition de l'énumération:

public static Stream<Letter> stream() {
    return Arrays.stream(Letter.values());
}

Et puis remplacez le pour d'en haut par:

Letter.stream().forEach(l -> foo(l));

Cette approche est-elle satisfaisante ou présente-t-elle un problème de conception ou de performance? De plus, pourquoi les enums n’ont-ils pas de méthode stream ()?

57
Rumal

Trois questions: réponse en trois parties:

Est-ce que ça va du point de vue de la conception?

Absolument. Rien de mal avec ça. Si vous devez effectuer de nombreuses itérations sur votre enum, l’API de flux est la méthode la plus propre. Cacher la plaque de la chaudière derrière une petite méthode est acceptable. Bien que je considère OldCumudgeon s version encore mieux.

Est-ce que ça va du point de vue de la performance?

Cela n’a probablement pas d’importance. La plupart du temps, les enums ne sont pas si gros. Par conséquent, le temps système qu’il ya pour une méthode ou une autre importe peu dans 99,9% des cas.

Bien sûr, il y a le 0,1% où il fait. Dans ce cas: mesurez correctement, avec vos données réelles et vos consommateurs.

Si je devais parier, je m'attendrais à la for each boucle pour être plus rapide, car elle correspond plus directement au modèle de mémoire, mais ne vous y attendez pas lorsqu’on parle de performance, et n’accordez pas avant d’avoir réellement besoin d’être peaufinée. Écrivez votre code de manière correcte en premier lieu, facile à lire en second lieu, puis ne vous souciez que de la performance du style de code.

Pourquoi Enums n’est-il pas correctement intégré à l’API Stream?

Si vous comparez l’API Java de l’API à l’équivalent dans de nombreux autres langages, elle apparaît sérieusement limitée. Il manque plusieurs éléments (Flux réutilisables et Optionals en tant que flux, par exemple). D'autre part, la mise en œuvre de l'API Stream constituait certainement un changement majeur pour l'API. Il a été reporté plusieurs fois pour une raison. J'imagine que Oracle souhaitait limiter les modifications aux cas d'utilisation les plus importants. Les Enums ne sont pas beaucoup utilisés de toute façon. Bien sûr, chaque projet en a plusieurs, mais ce n’est rien en comparaison du nombre de listes et d’autres collections. Même lorsque vous avez un Enum, dans de nombreux cas, vous ne le parcourrez jamais. Les listes et les ensembles, par contre, sont probablement itérés presque à chaque fois. Je suppose que ce sont les raisons pour lesquelles les Enums n’ont pas eu leur propre adaptateur pour le monde de Stream. Nous verrons si cela sera ajouté dans les versions futures. Et jusque-là, vous pouvez toujours utiliser Arrays.stream.

26
Jens Schauder

J'irais pour EnumSet . Étant donné que forEach() est également défini sur Iterable, vous pouvez éviter de créer complètement le flux:

EnumSet.allOf(Letter.class).forEach(x -> foo(x));

Ou avec une référence de méthode:

EnumSet.allOf(Letter.class).forEach(this::foo);

Reste que la boucle for oldschool est un peu plus simple:

for (Letter x : Letter.values()) {
    foo(x);
}
27
Natix

Mon suppose est que les enums sont limités en taille (c’est-à-dire que la taille n’est pas limitée par la langue mais par l’utilisation) et qu’ils n’ont donc pas besoin d’un natif stream api. Les flux sont très bons lorsque vous devez manipuler, transformer et rappeler les éléments d’un flux; ce ne sont pas des cas d'utilisation courants pour Enum (en général, vous parcourez des valeurs d'énum, ​​mais vous devez rarement les transformer, les mapper et les collecter).

Si vous avez seulement besoin de faire une action sur chaque élément, vous devriez peut-être n’exposer qu’une méthode forEach.

     public static void forEach(Consumer<Letter> action) {
            Arrays.stream(Letter.values()).forEach(action);
     }

     .... //example of usage
     Letter.forEach(e->System.out.println(e));
12
Giovanni

Je pense que le code le plus court pour obtenir un Stream de constantes d’énum est Stream.of(Letter.values()). Ce n'est pas aussi agréable que Letter.values().stream() mais c'est un problème avec les tableaux, pas spécifiquement enum.

De plus, pourquoi les enums n’ont-ils pas de méthode stream()?

Vous avez raison de dire que l'appel le plus agréable possible serait Letter.stream(). Malheureusement, une classe ne peut pas avoir deux méthodes avec la même signature, il ne serait donc pas possible d'ajouter implicitement une méthode statique stream() à chaque énum (de la même manière que chaque énum a une méthode statique implicitement ajoutée values()) car cela briserait toutes les énumérations existantes ayant déjà une méthode statique ou une méthode d'instance sans paramètre appelé stream().

Cette approche est-elle acceptable?

Je le pense. L'inconvénient est que stream est une méthode statique, il n'y a donc aucun moyen d'éviter la duplication de code; il faudrait l'ajouter à chaque enum séparément.

5
Paul Boddington