J'ai un List<Item>
collection. J'ai besoin de le convertir en Map<Integer, Item>
La clé de la carte doit être l’index de l’élément de la collection. Je ne peux pas comprendre comment faire cela avec des flux. Quelque chose comme:
items.stream().collect(Collectors.toMap(...));
De l'aide?
Comme cette question est identifiée comme une duplication possible, je dois ajouter que mon problème concret était de savoir comment obtenir la position de l'élément dans la liste et la définir comme valeur clé.
Vous pouvez créer un Stream
des index en utilisant un IntStream
, puis les convertir en un Map
:
Map<Integer,Item> map =
IntStream.range(0,items.size())
.boxed()
.collect(Collectors.toMap (i -> i, i -> items.get(i)));
Une autre solution, par souci d'exhaustivité, consiste à utiliser un collecteur personnalisé:
public static <T> Collector<T, ?, Map<Integer, T>> toMap() {
return Collector.of(HashMap::new, (map, t) -> map.put(map.size(), t),
(m1, m2) -> {
int s = m1.size();
m2.forEach((k, v) -> m1.put(k+s, v));
return m1;
});
}
Usage:
Map<Integer, Item> map = items.stream().collect(toMap());
Cette solution est compatible avec les parallèles et ne dépend pas de la source (vous pouvez utiliser list sans accès aléatoire ou Files.lines()
ou autre).
Ne vous sentez pas obligé de faire tout dans/avec le flux. Je voudrais juste faire:
AtomicInteger index = new AtomicInteger();
items.stream().collect(Collectors.toMap(i -> index.getAndIncrement(), i -> i));
Tant que vous ne paralléliserez pas le flux, cela fonctionnera et évitera des opérations potentiellement coûteuses et/ou problématiques (dans le cas de doublons) get()
et indexOf()
.
(Vous ne pouvez pas utiliser une variable int
régulière à la place de AtomicInteger
, car les variables utilisées en dehors d'une expression lambda doivent être effectivement définitives. Notez que lorsque non contesté (comme dans ce cas), AtomicInteger
est très rapide et ne pose pas de problème de performances, mais si cela vous inquiète, vous pouvez utiliser un compteur non thread-safe.)
Ceci est la réponse mise à jour et n'a aucun des problèmes mentionnés dans les commentaires.
Map<Integer,Item> outputMap = IntStream.range(0,inputList.size()).boxed().collect(Collectors.toMap(Function.identity(), i->inputList.get(i)));
réponse d'Eran est généralement la meilleure approche pour les listes à accès aléatoire.
Si votre List
n’est pas un accès aléatoire, ou si vous avez un Stream
au lieu d’un List
, vous pouvez utiliser forEachOrdered
:
Stream<Item> stream = ... ;
Map<Integer, Item> map = new HashMap<>();
AtomicInteger index = new AtomicInteger();
stream.forEachOrdered(item -> map.put(index.getAndIncrement(), item));
Ceci est sûr, si le flux est parallèle, même si la carte de destination est thread-unsafe et est utilisée comme un effet secondaire. Le forEachOrdered
garantit que les éléments sont traités un par un, dans l'ordre. Pour cette raison, il est improbable que l'exécution en parallèle entraîne une accélération. (Il peut y avoir une certaine accélération si des opérations coûteuses sont en cours avant le forEachOrdered
.)
En utilisant une bibliothèque tierce partie ( protonpack par exemple, mais il y en a d'autres), vous pouvez Zip
la valeur avec son index et le tour est joué:
StreamUtils.zipWithIndex(items.stream())
.collect(Collectors.toMap(Indexed::getIndex, Indexed::getValue));
bien que getIndex
renvoie un long
, vous devrez peut-être le lancer à l'aide de quelque chose de similaire à:
i -> Integer.valueOf((int) i.getIndex())