Warum kann ich beim Streaming von einem Array keine Ganzzahlen Zeichenfolgen zuordnen?

90

Dieser Code funktioniert (im Javadoc aufgenommen):

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

Dieser kann nicht kompiliert werden:

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

IDEA sagt mir, dass ich einen "inkompatiblen Rückgabetyp String im Lambda-Ausdruck" habe.

Warum ? Und wie kann man das beheben?

Denys Séguret
quelle

Antworten:

113

Arrays.stream(int[])schafft ein IntStream, nicht ein Stream<Integer>. Sie müssen also mapToObjstatt nur aufrufen map, wenn Sie intein Objekt einem Objekt zuordnen.

Dies sollte wie erwartet funktionieren:

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

was du auch schreiben kannst:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));
Assylien
quelle
3
Was ist der Unterschied zwischen IntStreamund Stream<Integer>?
Florian Margaine
8
@FlorianMargaine An IntStreamist eine Stream-Spezialisierung für primitive intWerte. A Stream<Integer>ist nur ein Stream, der IntegerObjekte enthält.
Alexis C.
2
@FlorianMargaine IntStreamist ein Stream oder Primitive (Ints), während Steram<Integer>es sich um einen Stream von Objekten handelt. Primitive Streams verfügen aus Leistungsgründen über spezielle Methoden.
Assylias
7
IntStream.mapToObjerwartet eine IntFunction, eine Funktion, die einen intWert verbraucht, funktioniert daher  .mapToObj((Integer i) -> i.toString())nicht. Es wird sowieso nicht empfohlen, da es eine unnötige Konvertierung von intnach enthält Integer. Im Gegensatz dazu .mapToObj(Integer::toString)funktioniert es gut, da es die staticMethode aufruft Integer.toString(int). Beachten Sie, dass dies anders ist als das Aufrufen .map(Integer::toString)von a,  Stream<Integer>da dieses nicht kompiliert werden kann, da es nicht eindeutig ist.
Holger
1
@cic: nein, das Aufrufen .map(Integer::toString)von a Stream<Integer>ist als beides wirklich mehrdeutig .map(i->i.toString())und .map(i->Integer.toString(i))gültig. Aber es ist leicht lösbar mit .map(Object::toString).
Holger
19

Arrays.stream(numbers)Erstellt eine IntStreamunter der Haube und die Kartenoperation auf einer IntStreamerfordert eine IntUnaryOperator(dh eine Funktion int -> int). Die Zuordnungsfunktion, die Sie anwenden möchten, berücksichtigt diesen Vertrag und damit den Kompilierungsfehler nicht.

Sie müssten boxed()vorher anrufen , um eine zu erhalten Stream<Integer>(dies ist, was Arrays.asList(...).stream()zurückkehrt). Rufen mapSie dann einfach an, wie Sie es im ersten Snippet getan haben.

Beachten Sie, dass , wenn Sie brauchen , boxed()gefolgt von mapIhnen wahrscheinlich verwenden möchten mapToObjdirekt.

Der Vorteil ist, dass mapToObjnicht jeder intWert einem IntegerObjekt zugeordnet werden muss. abhängig von der Mapping-Funktion wenden Sie natürlich an; also würde ich mich für diese Option entscheiden, die auch kürzer zu schreiben ist.

Alexis C.
quelle
5

Sie können einen Integer-Stream mit Arrays.stream (int []) erstellen, den Sie mapToObjwie aufrufen können mapToObj(Integer::toString).

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

Hoffe das hilft..

Codebot
quelle
2

Kein Boxen, AFAIK und keine Explosion kleiner Fäden, die dem Haufen hinzugefügt wurden:

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

Wenn der Zweck dieses Beispiels und dieser Frage darin besteht, herauszufinden, wie Zeichenfolgen einem Strom von Ints zugeordnet werden (z. B. Verwenden eines Stroms von Ints, um auf einen Index in einem Array von Zeichenfolgen zuzugreifen), können Sie auch Boxing und anschließend Casting verwenden ein int (das dann den Zugriff auf den Index des Arrays ermöglichen würde).

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

Der Aufruf von .boxed () konvertiert Ihren IntStream (einen Stream primitiver Ints) in einen Stream (einen Stream von Objekten - nämlich Integer-Objekte), der dann die Rückgabe eines Objekts (in diesem Fall eines String-Objekts) von akzeptiert dein Lambda. Hier ist es nur eine Zeichenfolgendarstellung der Zahl zu Demonstrationszwecken, aber es könnte genauso einfach (und praktischer) jedes Zeichenfolgenobjekt sein - wie das Element eines Zeichenfolgenarrays, wie zuvor erwähnt.

Ich dachte nur, ich würde eine andere Möglichkeit anbieten. Bei der Programmierung gibt es immer mehrere Möglichkeiten, eine Aufgabe zu erfüllen. Kennen Sie so viele wie möglich und wählen Sie dann diejenige aus, die für die jeweilige Aufgabe am besten geeignet ist. Beachten Sie dabei Leistungsprobleme, Intuitivität, Klarheit des Codes, Ihre Vorlieben im Codierungsstil und die Selbstdokumentation.

Viel Spaß beim Codieren!

jamesc1101
quelle
1
Du machst unnötige Arbeit. Sie verpacken jeden intnach seinem Wrapper-Typ Integerund entpacken ihn gleich danach.
Alexis C.