Java 8 chemin du travail avec un enum
Je me demandais quelle est la meilleure façon est de Java 8 de travailler avec toutes les valeurs d'une énumération. En particulier lorsque vous avez besoin pour obtenir toutes les valeurs et l'ajouter à quelque part, par exemple, en supposant que nous en avons l'enum ci-dessous:
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 pour l'enum définition:
public static Stream<Letter> stream() {
return Arrays.stream(Letter.values());
}
Et puis remplacez le pour de ci-dessus avec:
Letter.stream().forEach(l -> foo(l));
Cette approche est OK ou faut-il une faute dans la conception ou la performance? En outre, pourquoi ne pas les enums un stream() la méthode?
- Pourquoi ne pas ajouter une méthode
static void forEach(Consumer<Letter> action)
à votre enum? Ensuite, vous pouvez simplement appeler avecLetter.forEach(l -> foo(l))
. - Cela semble exagéré pour moi. Comme les tableaux ne prennent pas en charge le flux de l'API, je voudrais juste utiliser une boucle for et faire. Il semble que vous soyez en ajoutant de la complexité juste pour être nouveaux et brillant, mais il n'est pas de gagner quoi que ce soit ici.
- Cela ressemble à de l'optimisation prématurée. Je ne recommande pas de faire une référence pour votre scénario spécifique et de la technologie de la pile en utilisant JMH ou l'Étrier et d'utiliser la meilleure approche.
- De la première à obtenir ce travail, puis aller pour les optimisations de performances, et non pas l'inverse.
- c'est l'une des choses que je pensais, si dans un effort pour rendre le code le plus java8 façon je n'étais pas "tuer une mouche avec un bazooka". Mais en revanche, le code avec le flux de la méthode est mieux 😉
- En aparté,
forEach(l -> foo(l))
peut-être mieux écrit queforEach(foo)
. - C#, les gens se déplacent difficilement lorsque vous faites cela - pourquoi.
- Noter que l'appel
Enum.values
retourne unEnum[]
. Comme un tableau est mutable l'enum
doit de retour d'une autre copie de la matrice à chaque fois (juste au cas où quelqu'un muté il), ce qui signifie que tout appel àEnum.values
doit êtreO(n)
. C'est une faille dans la conception deenum
- immuableSet
aurait été un bien meilleur choix, mais cela signifie que si le rendement est primordial, alors vous devriez cacheEnum.value
dans une immuableSet
et qui offre unstream()
méthode... - l'Araignée: ou tout simplement utiliser
EnumSet.allOf(Letter.class).stream()
qui va utiliser un tableau partagé sous le capot. Ou comme cette réponse les points, même n'a pas besoin d'unStream
lorsque toutes les OP veut, c'estforEach(…)
. - est-ce exact? Regarder les code source la
EnumSet
appelle uneabstract addAll
de la méthode étant donné queEnumSet
aussi mutable - ne serait-ce pas aussi êtreO(n)
? - l'Araignée: Non, ce n'est pas la norme Collection de
addAll
méthode, c'est un spécialenum
méthode spécifique. Il ne touche même pas un tableau. - J'ai écrit un rapide jmh référence pour essayer de mettre quelques chiffres pour tout cela. Étonnamment, il semble que
Enum.values()
est sensiblement plus rapide que les autres approches. Je suis peut-être abuser de JMH en quelque sorte...
Vous devez vous connecter pour publier un commentaire.
Trois questions: les trois partie de la réponse:
Est-il acceptable à partir d'une conception de point de vue?
Absolument. Rien de mal avec elle. Si vous avez besoin de faire beaucoup d'itération sur votre enum, le flux API est le propre de chemin à parcourir et de cacher la chaudière de la plaque de derrière un peu de méthode est très bien. Bien que je voudrais examiner OldCumudgeon’s version encore mieux.
Est-il acceptable d'un point de vue des performances?
Il est probable qu'il n'a pas d'importance. La plupart du temps, les énumérations ne sont pas très grandes. Par conséquent, les frais généraux, il est pour l'un ou l'autre n'a probablement pas d'importance dans 99,9% des cas.
Bien sûr, il y a l'0.1% où il n'. Dans ce cas: - de mesurer correctement, avec votre de données du monde réel et des consommateurs.
Si j'avais à parier, je m'attends à de la
for each
boucle pour être plus rapide, car elle correspond plus directement le modèle de mémoire, mais n'essayez pas de deviner quand on parle de performance, et de ne pas régler avant qu'il y est besoin réel pour le réglage. Écrivez votre code est correct d'abord, facile à lire la seconde et alors seulement vous soucier de la performance de style de code.Pourquoi ne sont pas les Énumérations, bien intégré dans le Flux de l'API?
Si vous comparez Java de l'API Stream à l'équivalent dans de nombreuses autres langues, il apparaît très limitée. Il existe différentes pièces manquantes (réutilisables, des Ruisseaux et des Options comme des Ruisseaux, par exemple). D'autre part, la mise en œuvre de l'API Stream est certainement un énorme changement pour l'API. Elle a été reportée à plusieurs reprises pour une raison. Donc je suppose que Oracle a voulu limiter les modifications apportées à l'utilisation la plus importante des cas. Les énumérations ne sont pas utilisés que beaucoup de toute façon. Bien sûr, chaque projet a un couple d'entre eux, mais ils ne sont rien par rapport au nombre de Listes et d'autres Collections. Même lorsque vous avez un Enum, dans de nombreux cas, vous ne jamais effectuer une itération sur elle. Les listes et les Ensembles, d'autre part, sont probablement itéré presque à chaque fois. Je suppose que ce sont les raisons pourquoi les Énumérations ne pas obtenir leur propre carte dans le Flux du monde. Nous allons voir si en plus de cela s'ajoute, dans de futures versions. Et d'ici là, vous pouvez toujours utiliser
Arrays.stream
.values()
en fait des clones de la matrice. Donc c'est une bonne affaire de l'attribution de l',arraycopy
ing, libérant ainsi qu'a aller sur. Probablement assez bien optimisé par la JVM, mais si il est possible d'écrireLetter[] allLetters = Letter.values()
, juste une fois, puis une boucle sur que, vous aurez gain de performance.J'irais pour
EnumSet
. Parce queforEach()
est également définie surIterable
, vous pouvez éviter de créer le flux tout à fait:Ou avec une méthode de référence:
Encore, le oldschool pour la boucle se sent un peu plus simple:
EnumSet
.EnumSet.allOf(Letter.class) .stream().filter(…).forEach(…)
, leEnumSet
est la solution préférée car elle permet d'éviter la création de la tableau temporaire, qui pourrait être plus coûteux que le légerStream
exemple (à moins que la machine détecte et élude le tableau comme sur la défensive “copie”, mais même alors, à l'aide deEnumSet
est sur le pair avec d'autres solutions)Mon deviner est que les énumérations sont limités en taille (je.e la taille n'est pas limitée par la langue, mais limitée par l'utilisation)et donc ils n'ont pas besoin d'un natif flux api. Les flux sont très bon quand il faut manipuler la transformer et de se rappeler des éléments dans un flux de données; ce ne sont pas des utilisations courantes cas pour Enum (généralement de vous faire une itération sur les valeurs de l'enum, mais rarement vous avez besoin de transformer, d'une carte et de les collecter).
Si vous avez seulement besoin de faire une action sur chacun des éléments peut-être vous devriez exposer qu'un forEach méthode
enum
est recommandé comme un remplacement pour, par exemple, les indicateurs de bits et des choses comme ça. Ceci est facilité par leEnumSet
.System.out::println
est plus idiomatique.Je pense que le plus court de code pour obtenir un
Stream
d'enum constantes estStream.of(Letter.values())
. C'est pas aussi beau queLetter.values().stream()
mais c'est un problème avec les tableaux, et pas spécifiquement les enums.Vous avez raison, la plus belle possible appel serait
Letter.stream()
. Malheureusement, une classe ne peut pas avoir deux méthodes avec la même signature, de sorte qu'il ne serait pas possible de implicitement ajouter une méthode statiquestream()
à chaque enum (de la même manière que tous les enum a ajouté implicitement méthode statiquevalues()
) ce qui permettrait de briser tous les enum qui a déjà une statique de l'instance ou de la méthode sans paramètres appelésstream()
.Je pense que oui. L'inconvénient est que
stream
est une méthode statique, il n'existe aucun moyen d'éviter la duplication de code, elle aurait dû être ajouté à chaque enum séparément.