folgende Aufgabe stellt sich mir:
Ein dreidimensionalen Array, welches Objekte speichert soll nach bestimmten Kriterien gefiltert werden.
Ein bestimmter Wert der enthaltenen Objekte soll addiert werden wenn das Filterkriterium mit einer bestimmten Eigenschaft des Objekts übereinstimmt.
Lösungsideen:
Für eine ähnliche Aufgabenstellung bei Verwendung eines eindimensionales Array konnte ich das Problem mit Lambda-Expressions lösen.
public int getAnzahlSchraubenGesamt(Karton karton[], int laenge)
{
int anzahlGes = Arrays.stream(karton)
.filter( a -> (a != null) && ( a.getLaenge() == laenge ))
.mapToInt(a -> a.getAnzahlSchrauben()).sum();
return anzahlSchraubenGes;
}
Jetzt muss diese Art Filter auf ein dreidimensionales Array angewendet werden.
Leider habe ich keine Ahnung wie ich dies mit Hilfe von Lambda-Expressions lösen könnte.
Auch nach längerer Suche im Internet fand ich keine Hinweise, die mir bei der Problemlösung helfen konnten.
Ich bin am Überlegen, ob ich das Problem nicht einfach mit verschachtelten Schleifendurchläufen lösen sollte, ganz ohne Lambda.
Allerdings bin ich mir nicht sicher ob dies nicht eleganter, schlanker und performanter geht.
Für Anregungen, Ideen und Hilfen wäre ich der Community sehr dankbar.
ob stream + filter + mapToInt + sum gegenüber einer Schleife mit if und += eleganter ist stellt sich als Frage schon bei 1D, aber bitte,
Performance ist davon nebenei gewiss nicht zu erwarten, aber mag zurückstehen und im Nanobereich eh egal
zwei Strategien fallen mir für dich ein:
ein 2D-Array als Stream von 1D-Arrays durchlaufen und für jedes dieser 1D-Array die bisherige Methode getAnzahlSchraubenGesamt() aufrufen
(.mapToInt(oneDArray -> getAnzahlSchraubenGesamt(oneDArray , laenge))?), summieren
ein 2D-Array in einen zigfach längeren 1D-Stream umwandeln, gewiss Standardfunktion oder für sich eine Aufgabe, danach dann wie bisher summieren
für höhere D jeweils entsprechend weiter zu denken
so ist mal wieder eine Kinderübung für fast ersten Tag Java in komplizierte Frage komplizierter Syntax + API-Methoden verwandelt
Vielen Dank Ihr zwei.
Der Tipp mit der Kaskadenlösung ist irgendwie einleuchtend - funktioniert aber leider nicht.
Die 3D-2D-1D-Lambda-Lösung ist mir nicht einleuchtend.
Daher werde ich wohl den konventionellen Lösungsansatz mit den verschachtelten Schleifen wählen.
so schön auch zu hören ist das einfache Aufgeben ja doch nicht wünschenswert,
was funktioniert nicht?
soweit an Information gegeben, also das Karton-Beispiel, dürfte es gehen:
public static void main(String[] args) {
Karton[][][] ka = new Karton[2][3][4];
for (int i = 0; i < ka.length; i++)
for (int j = 0; j < ka**.length; j++)
for (int k = 0; k < ka**[j].length; k++)
ka**[j][k] = new Karton();
System.out.println(getAnzahlSchraubenGesamt(ka, 1));
}
public static int getAnzahlSchraubenGesamt(Karton[][][] karton, int laenge) {
int anzahlGes = Arrays.stream(karton).flatMap(Stream::of).flatMap(Stream::of)
.filter(a -> (a != null) && (a.getLaenge() == laenge)).mapToInt(a -> a.getAnzahlSchrauben()).sum();
return anzahlGes;
}
}
class Karton {
int getLaenge() {
return 1;
}
int getAnzahlSchrauben() {
return 5;
}
}
[quote=SlaterB]ab 2D sieht es schwieriger aus:
Initializing 2d array with streams in Java - Stack Overflow[/quote]Die lösung ließe sich doch recht simpel verallgemeinern. Die return-Zeile wird halt mit jeder Dimension immer länger…
Andererseits sind die geschachtelten Loops nun auch nicht sooo hässlich…
@SlaterB :
Recht herzlichen Dank für deine Mühe!
Dein Lösungsvorschlag funktioniert wunderbar.
Mir ist ein peinlicher Schnitzer unterlaufen.
Ich vergaß “java.util.stream.Stream” zu importieren.
@CyborgBeta :
Auch dir danke ich sehr für die von dir veröffentlichten Links.
Was sind denn die Dimensionen dieses Arrays? Den Klassennamen nach könnten es räumlich gestapelte Kartons sein, im Sinne von
int x = 1;
int y = 6;
int z = 8;
Karton k = kartons[x][y][z];
Andernfalls klingt ein 3D-Array von Referenzen ist schon sehr suspekt … also, es klingt nach etwas, was je nach Ziel und Nutzungs- und Anwendungsfall vielleicht hinter einem
interface Boxes {
Karton get(int x, int y, int z);
// oder auch, mit geeigneter "Point3D"-Klasse
Karton get(Point3D coordinates);
Stream<Karton> getStream();
}
versteckt werden könnte.
Ansonsten ist es wohl nicht verkehrt, das “Flachklopfen” als dedizierten Schritt rauszuzuiehen
package bytewelt;
import java.util.Arrays;
import java.util.stream.Stream;
public class ArrayStream3D
{
public static void main(String[] args)
{
Karton[][][] ka = new Karton[2][3][4];
for (int i = 0; i < ka.length; i++)
for (int j = 0; j < ka**.length; j++)
for (int k = 0; k < ka**[j].length; k++)
ka**[j][k] = new Karton();
System.out.println(getAnzahlSchraubenGesamt(ka, 1));
}
// Praktisch genau die Methode, die du ursprünglich hattest, nur wird
// direkt der Stream übergeben:
private static int getAnzahlSchraubenGesamt(
Stream<? extends Karton> kartons, int laenge)
{
return kartons
.filter(a -> (a != null) && (a.getLaenge() == laenge))
.mapToInt(a -> a.getAnzahlSchrauben()).sum();
}
private static int getAnzahlSchraubenGesamt(Karton[][][] karton, int laenge)
{
return getAnzahlSchraubenGesamt(flatten(karton), laenge);
}
private static <T> Stream<T> flatten(T ts[][][])
{
return Arrays.stream(ts).flatMap(t -> flatten(t));
}
private static <T> Stream<T> flatten(T ts[][])
{
return Arrays.stream(ts).flatMap(Stream::of);
}
}
class Karton
{
int getLaenge()
{
return 1;
}
int getAnzahlSchrauben()
{
return 5;
}
}
Vielleicht noch ein halb-off-topic-Nachtrag: Ich hatte mal eine kleine Lib erstellt für mehrdimensionale Arrays von Primitiven (!) Datentypen, und allgemein stellt sich bei solchen N-Dimensionalen Arrays auch die Frage nach der Reihenfolge, in der iteriert wird: https://github.com/javagl/ND/tree/master/nd-iteration#iteration-order Aber hier ist das vermutlich egal…