J'ai une bibliothèque tierce qui me donne un Enumeration<String>
. Je veux travailler avec cette énumération paresseusement en tant que Java 8 Stream
, en appelant des choses comme filter
, map
et flatMap
dessus.
Y a-t-il une bibliothèque existante qui contient cela? Je fais déjà référence à Guava et Apache Commons, donc si l'un ou l'autre a la solution qui serait idéale.
Sinon, quelle est la meilleure façon/la plus simple de transformer un Enumeration
en Stream
tout en conservant la nature paresseuse de tout?
Cette réponse fournit déjà une solution qui crée un Stream
à partir d'un Enumeration
:
public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) { return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<T>() { public T next() { return e.nextElement(); } public boolean hasNext() { return e.hasMoreElements(); } }, Spliterator.ORDERED), false); }
Il convient de souligner que le Stream
résultant est aussi paresseux que tout autre Stream
, car il ne sera pas traité tous les éléments avant le début de l'action du terminal et si le fonctionnement du terminal est en court-circuit, il itérera uniquement autant d'éléments que nécessaire.
Pourtant, elle peut encore être améliorée. J'ajouterais toujours une méthode forEachRemaining
lorsqu'il existe un moyen simple de traiter tous les éléments. Cette méthode sera appelée par l'implémentation Stream
pour la plupart des opérations sans court-circuit:
public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
new Iterator<T>() {
public T next() {
return e.nextElement();
}
public boolean hasNext() {
return e.hasMoreElements();
}
public void forEachRemaining(Consumer<? super T> action) {
while(e.hasMoreElements()) action.accept(e.nextElement());
}
},
Spliterator.ORDERED), false);
}
Cependant, le code ci-dessus est victime de l'anti-modèle "utilisant Iterator
parce qu'il est si familier". Le Iterator
créé sera enveloppé dans une implémentation de la nouvelle interface Spliterator
et ne fournit aucun avantage par rapport à l'implémentation de Spliterator
directement:
public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) {
public boolean tryAdvance(Consumer<? super T> action) {
if(e.hasMoreElements()) {
action.accept(e.nextElement());
return true;
}
return false;
}
public void forEachRemaining(Consumer<? super T> action) {
while(e.hasMoreElements()) action.accept(e.nextElement());
}
}, false);
}
Au niveau du code source, cette implémentation est aussi simple que celle basée sur Iterator
, mais élimine la délégation d'un Spliterator
à un Iterator
. Il suffit à ses lecteurs de se renseigner sur la nouvelle API.
Pourquoi ne pas utiliser Vanilla Java:
Collections.list(enumeration).stream()...
Cependant, comme mentionné par @MicahZoltu, le nombre d'éléments dans l'énumération doit être pris en compte, comme Collections.list
va d'abord parcourir l'énumération pour copier les éléments dans un ArrayList
. De là, la méthode régulière stream
peut être utilisée. Bien que cela soit habituel pour de nombreuses opérations de flux de collection, si l'énumération est trop grande (comme infinie), cela peut poser problème car l'énumération doit être transformée en liste, puis les autres approches décrites ici doivent être utilisées à la place.
Dans Java 9, il est possible de convertir un Enumeration
en Stream
avec une ligne:
Enumeration<String> en = ... ;
Stream<String> str = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(en.asIterator(), Spliterator.ORDERED),
false
);
(Eh bien, c'est une longue file.)
Si vous n'êtes pas sur Java 9, vous pouvez convertir le Enumeration
en Iterator
manuellement en utilisant la technique indiquée dans Holger's answer =.
Selon Guava docs , vous pouvez utiliser la méthode Iterators.forEnumeration()
:
Enumeration<Something> enumeration = ...;
Iterator<SomeThing> iterator = Iterators.forEnumeration(enumeration);
Et dans cette question , il est expliqué comment obtenir un flux depuis un itérateur:
Stream<Something> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
iterator, Spliterator.ORDERED),
false);
Dans ma bibliothèque StreamEx il y a une méthode simple StreamEx.of(Enumeration)
qui fait le travail:
Stream<String> stream = StreamEx.of(enumeration);
Notez qu'il ne s'agit pas seulement d'un raccourci vers la solution @Holger, mais implémenté de manière différente. En particulier, il présente des caractéristiques d'exécution parallèle nettement meilleures par rapport aux solutions impliquant Spliterators.spliteratorUnknownSize()
.