¿Por qué no puedo asignar números enteros a cadenas cuando transmito desde una matriz?

92

Este código funciona (tomado en el Javadoc):

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

Este no se puede compilar:

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

IDEA me dice que tengo un "tipo de retorno incompatible String en expresión lambda".

Por qué ? ¿Y cómo solucionarlo?

Denys Séguret
fuente

Respuestas:

114

Arrays.stream(int[])crea un IntStream, no un Stream<Integer>. Por lo que debe llamar en mapToObjlugar de solo map, al mapear unint a un objeto.

Esto debería funcionar como se esperaba:

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

que también puedes escribir:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));
Assylias
fuente
3
¿Cuál es la diferencia entre IntStreamy Stream<Integer>?
Florian Margaine
8
@FlorianMargaine An IntStreames una especialización de flujo para intvalores primitivos . A Stream<Integer>es solo un Stream que contiene Integerobjetos.
Alexis C.
2
@FlorianMargaine IntStreames una corriente o primitivas (ints) mientras que Steram<Integer>es una corriente de objetos. Las corrientes primitivas tienen métodos especializados por motivos de rendimiento.
assylias
7
IntStream.mapToObjespera una IntFunction, una función que consume un intvalor, por .mapToObj((Integer i) -> i.toString())lo tanto  , no funciona. De todos modos, no se recomendaría ya que contiene una conversión innecesaria de inta Integer. Por el contrario, .mapToObj(Integer::toString)funciona bien ya que llamará al staticmétodo Integer.toString(int). Tenga en cuenta que esto es diferente a llamar .map(Integer::toString)a a,  Stream<Integer>ya que este último no se compilará porque es ambiguo.
Holger
1
@cic: no, llamando .map(Integer::toString)en una Stream<Integer>es realmente ambigua ya que ambos, .map(i->i.toString())y .map(i->Integer.toString(i))son válidos. Pero se puede solucionar fácilmente usando .map(Object::toString).
Holger
19

Arrays.stream(numbers)crea un IntStreambajo el capó y la operación del mapa en un IntStreamrequiere un IntUnaryOperator(es decir, una función int -> int). La función de mapeo que desea aplicar no respeta este contrato y, por lo tanto, el error de compilación.

Debería llamar boxed()antes para obtener un Stream<Integer>(esto es lo que Arrays.asList(...).stream()devuelve). Luego, simplemente llame mapcomo lo hizo en el primer fragmento.

Tenga en cuenta que si necesita boxed()seguido de mapprobablemente desee utilizar mapToObjdirectamente.

La ventaja es que mapToObjno requiere encasillar cada intvalor en un Integerobjeto; dependiendo de la función de mapeo que aplique, por supuesto; así que optaría por esta opción, que también es más corta de escribir.

Alexis C.
fuente
5

Puede crear un Integer Stream usando Arrays.stream (int []), puede llamar a mapToObjlike mapToObj(Integer::toString).

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

Espero que esto ayude..

codebot
fuente
2

Sin boxeo, AFAIK, y sin explosión de pequeñas cuerdas agregadas al montón:

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
fuente
1

Si el propósito de esta muestra y pregunta es averiguar cómo asignar cadenas a una secuencia de ints (por ejemplo, usar una secuencia de ints para acceder a un índice en una matriz de cadenas), también puede usar el boxeo y luego enviar a un int (que luego permitiría acceder al índice de la matriz).

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

La llamada .boxed () convierte su IntStream (una secuencia de entradas primitivas) en una secuencia (una secuencia de objetos, es decir, objetos Integer) que luego aceptará la devolución de un objeto (en este caso, un objeto String) de tu lambda. Aquí es solo una representación de cadena del número con fines de demostración, pero podría ser con la misma facilidad (y de manera más práctica) cualquier objeto de cadena, como el elemento de una matriz de cadenas como se mencionó anteriormente.

Solo pensé en ofrecer otra posibilidad. En programación, siempre hay múltiples formas de realizar una tarea. Conozca tantos como pueda, luego elija el que mejor se adapte a la tarea en cuestión, teniendo en cuenta los problemas de rendimiento, la intuición, la claridad del código, sus preferencias en el estilo de codificación y la mayor documentación posible.

¡Feliz codificación!

jamesc1101
fuente
1
Estás haciendo un trabajo innecesario. Encuadra cada uno inten su tipo de envoltorio Integery lo desempaqueta justo después.
Alexis C.