Perché non riesco a mappare gli interi alle stringhe durante lo streaming da un array?


92

Questo codice funziona (preso in Javadoc):

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

Questo non può essere compilato:

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

IDEA mi dice che ho una "stringa di tipo restituito incompatibile nell'espressione lambda".

Perché ? E come risolverlo?

Risposte:


114

Arrays.stream(int[])crea un IntStream, non un file Stream<Integer>. Quindi è necessario chiamare mapToObjinvece di solo map, quando si mappa un intsu un oggetto.

Questo dovrebbe funzionare come previsto:

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

che puoi anche scrivere:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));

3
Qual è la differenza tra IntStreame Stream<Integer>?
Florian Margaine

8
@FlorianMargaine An IntStreamè una specializzazione del flusso per i intvalori primitivi . A Stream<Integer>è solo un flusso che contiene Integeroggetti.
Alexis C.

2
@FlorianMargaine IntStreamè un flusso o primitive (int) mentre Steram<Integer>è un flusso di oggetti. I flussi primitivi hanno metodi specializzati per motivi di prestazioni.
assylias

7
IntStream.mapToObjsi aspetta una IntFunction, una funzione che consuma un intvalore, quindi  .mapToObj((Integer i) -> i.toString())non funziona. Non sarebbe comunque consigliato in quanto contiene una conversione non necessaria da inta Integer. Al contrario, .mapToObj(Integer::toString)funziona bene poiché chiamerà il staticmetodo Integer.toString(int). Nota che questo è diverso dall'invocare .map(Integer::toString)a  Stream<Integer>poiché quest'ultimo non si compilerà perché è ambiguo.
Holger

1
@cic: no, chiamare .map(Integer::toString)a Stream<Integer>è veramente ambiguo in quanto entrambi .map(i->i.toString())e .map(i->Integer.toString(i))sono validi. Ma è facilmente risolvibile utilizzando .map(Object::toString).
Holger

19

Arrays.stream(numbers)crea un IntStreamsotto il cofano e l'operazione di mappatura su un IntStreamrichiede una IntUnaryOperator(cioè una funzione int -> int). La funzione di mappatura che si desidera applicare non rispetta questo contratto e quindi l'errore di compilazione.

Dovresti chiamare boxed()prima per ottenere un Stream<Integer>(questo è ciò che Arrays.asList(...).stream()ritorna). Quindi chiama mapcome hai fatto nel primo snippet.

Nota che se hai bisogno di boxed()seguito da mapprobabilmente vorrai usare mapToObjdirettamente.

Il vantaggio è che mapToObjnon richiede di inscatolare ogni intvalore in un Integeroggetto; a seconda della funzione di mappatura che applichi ovviamente; quindi sceglierei questa opzione che è anche più breve da scrivere.


5

Puoi creare un flusso intero utilizzando Arrays.stream (int []), puoi chiamare mapToObjlike mapToObj(Integer::toString).

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

Spero che questo ti aiuti..


2

Niente boxe, AFAIK, e nessuna esplosione di corde aggiunte al mucchio:

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);
}

1

Se lo scopo di questo esempio e domanda è capire come mappare le stringhe a un flusso di int (ad esempio, utilizzando un flusso di int per accedere a un indice in un array di stringhe), puoi anche usare il boxing, quindi eseguire il casting su un int (che consentirebbe quindi di accedere all'indice dell'array).

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

La chiamata .boxed () converte il tuo IntStream (un flusso di int primitivi) in un flusso (un flusso di oggetti - vale a dire, oggetti Integer) che quindi accetterà il ritorno di un oggetto (in questo caso, un oggetto String) da la tua lambda. Qui è solo una rappresentazione di stringa del numero a scopo dimostrativo, ma potrebbe essere altrettanto facilmente (e più praticamente) qualsiasi oggetto stringa, come l'elemento di un array di stringhe come menzionato prima.

Ho solo pensato di offrire un'altra possibilità. Nella programmazione, ci sono sempre più modi per svolgere un'attività. Conoscerne il maggior numero possibile, quindi scegliere quello che si adatta meglio all'attività da svolgere, tenendo presente i problemi di prestazioni, l'intuitività, la chiarezza del codice, le preferenze nello stile di codifica e la più auto-documentata.

Buona programmazione!


1
Stai facendo un lavoro non necessario. Si inscatola ciascuno intnel suo tipo di wrapper Integere lo si decomprime subito dopo.
Alexis C.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.