Java Stream: Ist es möglich, einen stream zusammenzufassen

Hey meine Frage wäre, wie ich einen Stream nach bestimmten Kriterien zusammenfasse. Beispielsweise habe ich eine Klasse mit:
[CODE lang="java" title="Klasse Entry"]private static class Entry {
private String state;
private int activeInfected;

Entry(String state, int activeInfected) {
this.state = state;
this.activeInfected = activeInfected;
}

//returns the name of the federal state of this entry (Bundesland)
String getState() {
return state;
}

int getActiveInfected() {
return activeInfected;
}
}[/CODE]

Nun kann es in dem Stream<Entry> dazu kommen, dass mehrere Bundesländer gleichzeitig im Stream stehen aber dafür unterschiedliche activeInfected hat.(Da der obere Code vereinfacht ist und es eigentlich noch städte Name und Datum gibt, kann es sein dass mehrere gleiche Bundesländer in dem Stream stehen, aber unterschiedliche activeInfected nummern haben).

Bis jetzt bin ich mit dem Stream soweit:
[CODE lang="java" title="Stream"]public static List<String> safestStateAsList(Stream<Entry> entryStream) {
return entryStream
.sorted(Comparator.comparing(activeInfected))
.collect(Collectors.groupingBy(Entry::getState))
//todo Bundesländer zusammenfassen
;
}


[/CODE]

Die Funktion activeInfected in sorted() sortiert den Stream nach aktiven Infektionszahlen.(Funktioniert!)

Die Einträge mit den gleichen state Namen wüde ich gerne zusammenfassen aber leider weiß ich nicht, wie ich das mache(also welche Methode ich benutzen soll). Am Ende soll ich sozusagen eine List<String> zurückgeben, die meine Bundesländer nach aktiven Infektionszahlen sortiert.
Bsp: {[state = Hessen, active infected = 3], [state = Bayern, active Infected = 20]} und nicht
{[state = Hessen, active infected = 3], [state = Bayern, active Infected = 7], [state = Bayern, active Infected = 13]}

Vielen Dank im vorraus.
 
Ist das nur eine Spielerei? generell würde ich so Code vermeiden. Wenn Du da mit irgendwelchen Tricks eine reine Stream Lösung hin bekommst, dann wird dies dennoch nicht intuitiv sein. Das bedeutet also, dass es schlecht lesbarer Code sein wird.

Daher wäre mein Ratschlag generell, dies zu vermeiden und statt dessen einfache Objektorientierte Lösungen zu entwerfen. Und dann wäre evtl. eine Klasse, in der so Daten verwaltet werden. Dort kannst Du dann von mir aus Entry Elemente hinzufügen (und da wird dann z.B. zusammen gefasst pro Bundesland...)

So Du da also wirklich einen Stream übergeben willst (Wenn das nicht vielleicht schon ein schlechtes Design ist? Aber dazu haben wir zu wenig Informationen. Siehe dazu ggf. Effective Java Thema 45: Setzen Sie Streams mit Bedacht ein), dann könnte diese Klasse z.B. auch als Collector dienen.

Generell: collect ist ein Ende des Streams. Das heißt Du bekommst das gesammelt Ergebnis zurück. Also durch dein Collectors.groupingBy bekommst Du eine Map zurück.

Du kannst aber ggf. die groupinBy Variante nutzen mit eigenem Collector - aber das sind Dinge, über die ich nicht einmal im Detail nachdenken möchte. Da kommt nichts lesbares, wieder verwendbares raus. Alles was man da produziert ist eine Bitte an Andere, dass diese einen wegen dem Code verhauen. (um es mal so vereinfacht auszudrücken).
 
Du könntest es z.B.: so löse. Ich finde übrigens nicht, dass der Code nicht lesbar oder unverständlich ist. Im Gegenteil, ich denke Schleifen mit den dazugehörigen "Treppen" sind Mist.

Java:
        ArrayList<Entry> arrayList = new ArrayList<>();
        arrayList.add(new Entry("1", 5));
        arrayList.add(new Entry("1", 4));
        arrayList.add(new Entry("1", 3));
        arrayList.add(new Entry("1", 2));
        arrayList.add(new Entry("1", 11));
        
        arrayList.add(new Entry("2", 5));
        arrayList.add(new Entry("2", 4));
        arrayList.add(new Entry("2", 3));
        arrayList.add(new Entry("2", 2));
        arrayList.add(new Entry("2", 12));
        
        arrayList.add(new Entry("3", 5));
        arrayList.add(new Entry("3", 4));
        arrayList.add(new Entry("3", 3));
        arrayList.add(new Entry("3", 2));
        arrayList.add(new Entry("3", 13));
        

        arrayList.stream()
                .collect(Collectors.groupingBy(
                        Entry::getState,
                        Collectors.reducing(0, Entry::getActiveInfected, (a,b) -> a + b)))
                
                .forEach((s,i) -> System.out.println(String.format("[state=%1$s, activeInfected=%2$s]", s,i)));
 
Im Gegenteil, ich denke Schleifen mit den dazugehörigen "Treppen" sind Mist.
Das ist auch nicht das, was ich als Lösung genannt habe.
einfache Objektorientierte Lösungen zu entwerfen
Umfasst deutlich mehr als einfach nur unleserlichen Code in Form mehrere verschachtelter Schleifen zu schreiben.

Aber jeder soll da seine Sicht haben und finden. Dieses "ich packe ganz viel Aktion in ganz wenig Code" magst Du cool finden - das Thema hatten wir im Forum auch schon oft genug. Aber wenn Du wirkliche Produkte hast, bei denen Du auch später noch am Code interessiert bist, dann wirst Du früher oder später auch zu der Erkenntnis kommen, dass es durchaus besser sein kann, Dinge etwas anders und "objektorientierter" zu schreiben. Aber da geht es dann ja auch um mehr als nur so kleine Ausschnitte.
 
Eine Ergänzung hätte ich da noch. Es macht aus meiner Sicht keinen Sinn den Stream erst nach activeInfected zu sortieren und dann zu gruppieren und zu summieren. Da danach die Sortierung nicht mehr stimmen muss.
 
Eine Ergänzung hätte ich da noch. Es macht aus meiner Sicht keinen Sinn den Stream erst nach activeInfected zu sortieren und dann zu gruppieren und zu summieren. Da danach die Sortierung nicht mehr stimmen muss.
Die Reihenfolge lässt sich durch eine LinkedHashMap als mapFactory einfach beibehalten. Ausserdem könnte man statt reducing(0, ...) auch `Collectors.summingInt()` (also die "Summe der Elemente als int") verwenden:
Dein Beispiel dahingehend abgewandelt:
Java:
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.*;
...
arrayList
    .stream()
    .sorted(comparing(Entry::getState))
    .collect(
        groupingBy(
            Entry::getState,
            LinkedHashMap::new,
            summingInt(Entry::getActiveInfected)))
    .forEach((s, i) -> System.out.println(String.format("[state=%1$s, activeInfected=%2$s]", s, i)));
 
Ich meinte nicht die Reihenfolge in der Map. Der Threadersteller sortiert zuerst
Java:
sorted(Comparator.comparing(activeInfected))
also nicht nach dem Bundesland. Wenn erst nach "activeInfected" sortiert wird und dann werden von "activeInfected" Summen gebildet, dann muss die Sortierung nicht mehr stimmen. Deshalb habe ich die Sortierung außen vorgelassen. Wenn nach Entry::getState sortiert werden soll, dann ist dein Beispiel der richtige Weg.
 

Zurück
Oben