Lesbarkeit von Streams

K

kneitzel

Gast
Hallo zusammen,

ich starte mal einen neuen Thread anstatt im anderen Thread zu antworten. Denn da bin ich dann eben über etwas gestolpert:
In diesem Forum wundert es mich schon fast, dass noch keiner mit Stream geschwungen hat :p

Java:
int[] temperaturen = {12, 14, 7, 12, 15, 16, 15, 15, 11, 8, 13, 13, 15, 12};

IntSummaryStatistics stats = IntStream.of(temperaturen).summaryStatistics();

System.out.printf("Durchschnittstemperatur: %.2f°C%n", stats.getAverage());
System.out.printf("Minimale Temperatur: %d°C%n", stats.getMin());
System.out.printf("Maximale Temperatur: %d°C%n", stats.getMax());

int[] maxDiffData = IntStream.range(1, temperaturen.length)
        .mapToObj(i -> new int[]{ i, Math.abs(temperaturen[i-1] - temperaturen[i]) })
        .max(Comparator.comparingInt(arr -> arr[1]))
        .get();

System.out.printf("Größter Umschwung von %d°C zwischen den Tagen %d und %d%n", maxDiffData[1], maxDiffData[0], maxDiffData[0] + 1);
(Bitte nicht ernst nehmen, ein Prof will bestimmt keine Streams sehen)

Ich nutze selbst gerne Streams von Zeit zu Zeit. Aber an ein paar Dingen störe ich mich doch immer wieder gerne mal.

Hier gab es ganz konkret zwei Dinge, die mir aufgefallen sind:
a) range - auf den ersten Blick habe ich mich gewundert. Wieso temperaturen.length? Klar - range / rangeClosed: Ersteres ist exklusive des Endes, Zweites ist incl. der Grenze. Dies empfinde ich nicht als wirklich intuitiv.

b) mapToObj - Sehr leistungsfähig und man kann einiges damit machen. Aber auch hier sehe ich das nicht wirklich als intuitiv lesbar an. (Dabei ist das hier auch noch ein sehr einfaches, übersichtliches Beispiel!)

Daher würde ich mir einmal die Frage erlauben, ob Ihr solche Konstrukte auch bei produktivem Code einsetzen würdet oder ob ihr da eher auf "klassischen" Code ausweichen würdet...

In meinem Code findet man Streams mehr bei einfachen Filter-, Sortier- und Aufruf-Aufgaben. Bei dem Beispiel hätte ich also auch eher eine Methode geschrieben, die das in einer Schleife herausfindet und dann den Treffer zurück gibt. Also wirklich 08/15 Java code :)
 

LimDul

Top Contributor
Ich mag streams mittlerweile sehr. Ich finde sie in der Regel auch sehr gut lesbar, selbst bei längeren Verkettungen weil man sie wirklich komplett von links nach rechts lesen kann (ggf. ein paar manuelle Zeilenumbrüche der Übersichtlichkeit wegen).

Vermeiden würde ich nur:
* Zu viel Code in Lambda Ausdrücken
* Mapping was über simples transformieren hinausgeht.

Beides kann man oft vermeiden, wenn man das in eigene Methoden auslagert.

Deswegen finde ich den Stream hier auch persönlich auch nicht wirklich toll. @JustNobody hat schon ein paar Punkte angesprochen:

* IntStream.range hab ich bisher nicht genutzt, daher müsste ich hier auch nachschauen warum keine OutOfBoundsException fliegt.
* Den Code in mapToObj hätte ich in eine eigene Methode ausgelagert und ihr entsprechend Namen gegeben z.B.tempDiffToPreviousElement

Nichts destotrotz hätte ich es hier auch eher mit einer for-schleife gelöst.
 

mihe7

Top Contributor
Da kann ich mich Euch anschließen, insbesondere an den Einsatz von Streams. Was die von @JustNobody angesprochene Methode mapToObj betrifft, so dient diese ja dazu, den primitiven Datentypen auf ein Objekt abzubilden. Finde ich durchaus ok - und auch noch gut zu verstehen. Die Lösung von @MoxxiManagarm ist diesbezüglich auch etwas kompliziert, hier reicht ein reduce.

Wenn man das alles noch in ein Objekt packt, wird der Code m. E. recht übersichtlich. Intuitiv verständlich finde ich ihn aber nicht. Darum würde ich das auch mit einer Schleife schreiben. Vielleicht ist aber gerade das der Fehler.

Java:
    private int[] temp = {12, 14, 7, 12, 15, 16, 15, 15, 11, 8, 13, 13, 15, 12};

    public void printMaxDiff() {
        int ix = IntStream.range(1,temp.length).reduce(1, this::maxDiffAt);
        System.out.printf("Größter Umschwung von %d °C zwischen den Tagen " +
            "%d und %d\n", diffAt(ix), ix, ix+1);
     }

    private int maxDiffAt(int ix1, int ix2) {
        return diffAt(ix1) > diffAt(ix2) ? ix1 : ix2;
    }

    private int diffAt(int ix) {
        return Math.abs(temp[ix] - temp[ix-1]);
    }
 

LimDul

Top Contributor
Ich glaube nach einmal drüber schlafen, kann ich jetzt besser begründen, was mich persönlich an der Stream Lösung stört.

Eingabe: Ein Array von Temperaturen
Der Stream geht aber nicht über das Array, sondern über den Index vom Array - und das sorgt zumindest bei mir dafür, dass ich genau hinschauen muss, was da passiert. Wenn ich ein Array habe, würde ich erwarten, dass der Stream über die Array Elemente geht. Das ist schließlich gerade die Datenstruktur die ich in der Hand habe. Und da der Code genau das nicht tut läuft er meiner Intuition entgegen was dazu führt, dass ich mich im Detail damit beschäftigen muss, wie nun der Stream mit dem Array verknüpft ist. Und wenn ich einen Stream über den Index mache - dann ist das im Prinzip eine for Schleife. Bei einer for-Schleife wird die Tatsache, das der Index wichtig ist, nochmal deutlich hervorgehoben - und damit der Code für mich intuitiv verständlicher.

Damit will ich meine ursprüngliche Aussage noch mal ergänzen:
* Streams sind toll, wenn ich eine Datenstruktur habe (Array, Collection etc.) und über die mittels Transformationen und Filtern iterieren will. Weil da ist auch sofort klar, was passiert. Eingabe: Datenstruktur und dann wird mit jedem Element was gemacht.
 

CSHW89

Bekanntes Mitglied
Ich denke, wenn man sich in das Thema einarbeitet, will man die Streams erstmal überall einsetzen, und sucht sich alle möglichen Einsatzmöglichkeiten. Oft passiert es da aber, dass man das Konzept ad absurdum führt. Dann entstehen genau solche Lösungen. Ich persönlich benutze z.B. IntStream.range gar nicht mehr, zumal ich mal ein Fall hatte, in dem ich sehr viele IntStreams erstellt hatte, mit kleinen Ranges. Die Performance war grauenhaft.

Sehr gerne benutze ich Streams z.B. bei Transformationen von Datenquellen in neue:
Java:
List<ObjectA> lst = ...
List<ObjectB> newLst = lst.stream()
    .filter(ObjectA::myFilter)
    .map(ObjectA::toB)
    .collect(Collectors.toList());
Sowas finde ich klar verständlich und leichter zu lesen, als eine Schleife, in der ich in die neue Liste aufbaue.

Ein anderes Einsatzgebiet, welches aber durchaus kontrovers diskutiert werden könnte, ist die Rückgabe von ReadOnly-Collections. Da ich nicht wirklich glücklich bin, mit den Möglichkeiten von Java diesbezüglich (RuntimeException bei Collections.unmodifiableXXX), benutze ich dort inzwischen Streams recht gerne. Zusätzlich kann man Filter einfach anbieten.
Java:
class MyClass {
    private List<Product> products = ...;
    public Stream<Product> getProducts() {
        return products.stream();
    }
    public Stream<Product> getActiveProducts() {
        return getProducts().filter(Product::isActive);
    }
}

Viele Grüße
Kevin
 
K

kneitzel

Gast
Danke für die Schilderung eurer Sicht und eures Vorgehens. Ich fand jede Antwort sehr informativ und auch wenn ich nicht im Detail antworte: Ich habe da sehr drüber nachgedacht und es hat mir geholfen, an meiner Sicht weiter zu feilen. Eine Zusammenfassung erspare ich mir, da die Positionen doch recht nah beieinander liegen und ich so nichts Neue beitragen würde.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Monokuma Lesbarkeit von Streams Allgemeine Java-Themen 6
mihe7 Lesbarkeit von Streams Allgemeine Java-Themen 5
G Stil-und Lesbarkeit des Codes Allgemeine Java-Themen 18
8u3631984 Frage zu Java Streams min / max Allgemeine Java-Themen 17
Z Mit Java 8+ Streams Zeilen nummern zu Zeilen hinzufügen Allgemeine Java-Themen 17
F Streams als Alternative für dieses Problem ? Allgemeine Java-Themen 15
Monokuma Foreach Schleifen in Streams umändern Allgemeine Java-Themen 23
J Streams Allgemeine Java-Themen 6
LimDul Streams und Exception Allgemeine Java-Themen 8
LimDul Mittels Streams aus Strings A B C den String A, B und C machen Allgemeine Java-Themen 12
X Ermittlung eines doppelte Paars mit Streams Allgemeine Java-Themen 50
N Streams wann .filtern? Allgemeine Java-Themen 2
A Lambda und Streams verstehen Allgemeine Java-Themen 4
J IO Streams Allgemeine Java-Themen 13
M Wie funktionieren parallele Java Streams? Allgemeine Java-Themen 1
R Collections BiPredicate in Java 8 Streams Allgemeine Java-Themen 7
F Umgehen mit übergebenen Streams Allgemeine Java-Themen 3
hdi Überwachen des err-Streams? Allgemeine Java-Themen 8
N read streams umleiten Allgemeine Java-Themen 2
DEvent GZIP Streams und gzip Allgemeine Java-Themen 2
S Streams zusammenfügen Allgemeine Java-Themen 4
J Objekt in Datei speichern mittels Streams Allgemeine Java-Themen 6
F Double mit Streams aus Datei einlesen Allgemeine Java-Themen 3
C in Streams Allgemeine Java-Themen 4
M Sind Streams asynchron? Allgemeine Java-Themen 2
Saxony DOS Konsole und deren Streams Allgemeine Java-Themen 5
B Probleme mit Streams Allgemeine Java-Themen 5
T Zwei Streams verbinden Allgemeine Java-Themen 3
L Die Wahl des richtigen Streams? Allgemeine Java-Themen 3
G Video Streams mit Servlets Allgemeine Java-Themen 3
P Streams per Mail mit JavaMailAPI versenden Allgemeine Java-Themen 3
A Streams: merkwürdiges Verhalten Allgemeine Java-Themen 7
A Streams - merkwürdiges Verhalten Allgemeine Java-Themen 2
thE_29 Problem mit Streams Allgemeine Java-Themen 6

Ähnliche Java Themen

Neue Themen


Oben