Pourquoi ne puis-je pas mapper des entiers sur des chaînes lors de la diffusion à partir d'un tableau?

92

Ce code fonctionne (pris dans le Javadoc):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));

Celui-ci ne peut pas être compilé:

int[] numbers = {1, 2, 3, 4};
String commaSeparatedNumbers = Arrays.stream(numbers)
    .map((Integer i) -> i.toString())
    .collect(Collectors.joining(", "));

IDEA me dit que j'ai une "chaîne de type de retour incompatible dans l'expression lambda".

Pourquoi ? Et comment y remédier?

Denys Séguret
la source

Réponses:

114

Arrays.stream(int[])crée un IntStream, pas un Stream<Integer>. Vous devez donc appeler mapToObjau lieu de simplement map, lorsque vous mappez un intà un objet.

Cela devrait fonctionner comme prévu:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(i -> ((Integer) i).toString()) //i is an int, not an Integer
    .collect(Collectors.joining(", "));

que vous pouvez également écrire:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));
assylies
la source
3
Quelle est la différence entre IntStreamet Stream<Integer>?
Florian Margaine
8
@FlorianMargaine An IntStreamest une spécialisation de flux pour les intvaleurs primitives . A Stream<Integer>est juste un Stream contenant des Integerobjets.
Alexis C.
2
@FlorianMargaine IntStreamest un flux ou des primitives (ints) alors que Steram<Integer>c'est un flux d'objets. Les flux primitifs ont des méthodes spécialisées pour des raisons de performances.
assylias
7
IntStream.mapToObjattend an IntFunction, une fonction qui consomme une intvaleur, donc  .mapToObj((Integer i) -> i.toString())ne fonctionne pas. Cela ne serait de toute façon pas recommandé car il contient une conversion inutile de intvers Integer. En revanche, .mapToObj(Integer::toString)fonctionne bien car il appellera la staticméthode Integer.toString(int). Notez que c'est différent de l'appel .map(Integer::toString)à a  Stream<Integer>car ce dernier ne se compilera pas car il est ambigu.
Holger
1
@cic: non, appeler .map(Integer::toString)a Stream<Integer>est vraiment ambigu comme les deux .map(i->i.toString())et .map(i->Integer.toString(i))est valide. Mais il est facilement résolu en utilisant .map(Object::toString).
Holger
19

Arrays.stream(numbers)crée un IntStreamsous le capot et l'opération cartographique sur un IntStreamnécessite un IntUnaryOperator(c'est-à-dire une fonction int -> int). La fonction de mappage que vous souhaitez appliquer ne respecte pas ce contrat et donc l'erreur de compilation.

Vous devrez appeler boxed()avant pour obtenir un Stream<Integer>(c'est ce qui Arrays.asList(...).stream()revient). Ensuite, appelez simplement mapcomme vous l'avez fait dans le premier extrait.

Notez que si vous avez besoin de boxed()suivi par mapvous souhaitez probablement utiliser mapToObjdirectement.

L'avantage est qu'il mapToObjn'est pas nécessaire de placer chaque intvaleur dans un Integerobjet; en fonction de la fonction de cartographie que vous appliquez bien sûr; donc j'irais avec cette option qui est également plus courte à écrire.

Alexis C.
la source
5

Vous pouvez créer un flux entier en utilisant Arrays.stream (int []), vous pouvez appeler mapToObjlike mapToObj(Integer::toString).

String csn = Arrays.stream(numbers) // your numbers array
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));

J'espère que cela t'aides..

codebot
la source
2

Pas de boxe, AFAIK, et pas d'explosion de petites cordes ajoutées au tas:

public static void main(String[] args) {
    IntStream stream = IntStream.of(1, 2, 3, 4, 5, 6);
    String s = stream.collect(StringBuilder::new, (builder, n) -> builder.append(',').append(n), (x, y) -> x.append(',').append(y)).substring(1);
    System.out.println(s);
}
AbuNassar
la source
1

Si le but de cet exemple et de cette question est de comprendre comment mapper des chaînes à un flux d'entiers (par exemple, en utilisant un flux d'entiers pour accéder à un index dans un tableau de chaînes), vous pouvez également utiliser la boxe, puis la conversion en un int (qui permettrait alors d'accéder à l'index du tableau).

int[] numbers = {0, 1, 2, 3}; 
String commaSeparatedNumbers = Arrays.stream(numbers)
    .boxed()
    .map((Integer i) -> Integer.toString((int)i))
    .collect(Collectors.joining(", "));

L'appel .boxed () convertit votre IntStream (un flux d'entiers primitifs) en un Stream (un flux d'objets - à savoir, des objets Integer) qui acceptera alors le retour d'un objet (dans ce cas, un objet String) de votre lambda. Ici, il s'agit simplement d'une représentation sous forme de chaîne du nombre à des fins de démonstration, mais cela pourrait tout aussi facilement (et plus pratiquement) être n'importe quel objet de chaîne - comme l'élément d'un tableau de chaînes comme mentionné précédemment.

Je pensais juste offrir une autre possibilité. En programmation, il existe toujours plusieurs façons d'accomplir une tâche. Apprenez-en autant que vous le pouvez, puis choisissez celui qui convient le mieux à la tâche à accomplir, en gardant à l'esprit les problèmes de performances, l'intuitivité, la clarté du code, vos préférences en matière de style de codage et le plus auto-documenté.

Bon codage!

jamesc1101
la source
1
Vous faites un travail inutile. Vous boxez chacun intà son type de wrapper Integeret vous le déballez juste après.
Alexis C.