Java Backend: Ist diese Methode zu komplex?

Ich denke es gibt Programmiersprachen, die sich besser und schlechter eigenen, lesbaren Code zu schreiben. Auch wenn ich manche dafür steinigen, weil sie es womöglich nicht als Programmiersprache sehen, ein Extrembeispiel für mich ist Assembler.

Und gerade als Datenbanker hat man denke ich wenig mit Sprachen zu tun, die sich dafür eignen. :-)

dms schrieb:
Letzter Schreih scheinbar - niecht hier im Thread - eine CHATGPT-Idee irgendwo "hinrotzen" und andere "Leute" sollen das dann gangbar machen
Yap, das ist auch das, was ich meine mit "Ich finde es viel schlimmer, wenn die Leute sich keine Gedanken machen."
 
  • Gefällt mir
Reaktionen: CyborgBeta
Vielleicht ist das auch ein allgemeines Problem, zwischen den Leuten, die eine Ausbildung gemacht haben (als FIAE zum Beispiel), und denen, die das studiert haben.
 
Das denke ich nicht. Programmieren lernt man nicht im Studium, genauso wie man Autofahren nicht in der Fahrschule lernt - sondern in der Praxis.

Das heißt nicht, dass es nicht Studenten geben kann, die gut programmieren können. Aber das hat meistens nichts mit dem Status "Student" zu tun.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Zurück zum ursprünglichen Problem:

- was wir hier haben ist ein klassisches X Y Problem. Eine Lösung für ein Problem, welches wir überhaupt nicht kennen.

- daß dir das um die Ohren gehauen werden würde war zu erwarten gewesen, weil
1. man guckt sich den Code an und sagt "aha...?"
2. man guckt sich die Signatur an und geht erstmal davon aus daß wir hier in Abhängigkeit des Inputs ein kubisches Speichervolumen bekommen brr 🥶
3. man guckt den Code nochmal an und sieht Indices, subindices und subsubindices und fragt sich... exakt was will der Autor uns damit sagen?

Deshalb, und nein für ein solches vergleichsweise klares Problem lese ich nicht seitenweise Forenposts, die da eigentlich gar nicht so viel sein dürften:

1. WAS ist das Problem -- was soll gelöst werden?
2. Was soll verarbeitet werden
3. was ist das erwartete Ergebnis

und soweit ich das aus den ersten Beiträgen entnehmen konnte wurde ja das Relevante durchaus schon gesagt, aber zur Sicherheit:
  • objekt orientiert heißt "punktuell am Problem" arbeiten, dh es wird in derselben Methode nicht alles gemeinsam gemacht und schon gar nicht nested, sondern man geht tatsächlich her und löst exakt EIN Teilproblem ---- wenn man gut ist, in einer Form, daß das parallelisierbar ist
  • KISS, kann man gar nicht genug betonen
  • wenn es um Tables geht, dann hat jede Table ein Schema und kann -- muß! --- in einem Objekt abgebildet werden; ob man da jetzt eine Table in ein Objekt steckt oder das nochmal zusammenfasst, da kann man je nach Problemstellung sich das überlegen, aber einfach ein multidimensionales struct hernehmen ist der völlig falsche Weg für OO

Am Ende des Tages werden Methoden "ganz von alleine" kurz und übersichtlich, läßt sich überhaupt nicht vermeiden, wenn man damit nur jeweils einen Input auf einen Output mappt. Klar kann das "kurz" dann auch mal paar hundert Zeilen werden, aber das ist halt nicht nur selten, sondern eben im Hinblick auf die Erfassung der Methode als Ganzes auch nicht weiter relevant.


Objektorientiert arbeiten heißt aber insbesondere eben auch sprechend arbeiten, und der Vorschlag oben ist nicht sprechend. Blackbox trifft es eher. Ziel ist nicht das Sparen von Codezeilen. Ziel ist die effektive Verarbeitung von Input, bzw die Bearbeitung eines Problems; und wenn das mit einer Zeile besser geht als mit einer weniger, dann nimmt man die Extrazeile.
 
  • Gefällt mir
Reaktionen: BeBur, VD90, dms und 2 andere
icked schrieb:
und wenn das mit einer Zeile besser geht als mit einer weniger, dann nimmt man die Extrazeile.
Öhm, nein, man schreibt keine unnötigen Zeilen. Das wäre ein Anti-Pattern (und ein Smell).

Im Deutschunterricht wären das Füllwörter oder -Sätze gewesen. Auch zu vermeiden. Dadurch wird ein Text länger, aber dessen Aussagewert nicht größer. Guckst du manchmal die Tagesschau oder liest Zeitungen? Fallen dir dort viele Füllwörter auf? Nein, weil diese keine Botschaft oder Gehalt oder Entropie haben. Einzig, wenn Korrespondenten nach etwas gefragt werden, beginnen sie ihren "Bericht" oft mit ja, nein, ich ..., also, oder anderen Interjektionen. Das ist hierbei aber eher ein (rhetorisches) Stilmittel, das der besseren Aussprache behilflich sein soll.
Ergänzung ()

Edit: Und allen benötigten Informationen stehen in diesem Thema. Nix mit XY.
 
Ich glaube Codeanforderungen variieren je nachdem wie viele Leute am Code arbeiten und ob der Code lange gepflegt werden muss oder eben nicht.

Ich finde es voll legitim, dass der TE bei seinem persönlichen Projekt möglichst Zeilen sparen mag, aber für eine Mehrheit an Menschen, die diesen Code dann interpretieren müssen/wollen ist eben doch "sprechender" Code und auch oftmals längerer Code besser nachvollziehbar.

Das sind keine "Füllwörter" oder sinnlose Zeilen, sondern kommt einfach natürlicher Sprache näher.

Ich gehe mal noch weiter: Wenn der in Post 1 gezeigte Code in einem hypothetisch immer komplexer werdenden Programm eben nach einiger Zeit doch mal für bspw. einen Bugfix angefasst werden muss, vermute ich, dass selbst der TE als Autor relativ lange brauchen würde diesen zu verstehen.

Ich glaube darum gibt es hier in der Diskussion und auch in dem javaForum gefühlt so viele Reibungspunkte.

Hier treffen eben verschiedene Codephilosophien (gibt es sowas überhaupt? 😅) aufeinander.

Für eine Frage in einen Forum würde ich persönlich immer "Cleancode" konformen Code bevorzugen und nicht möglichst kurzen. Aber das ist nur meine Meinung...
 
  • Gefällt mir
Reaktionen: KitKat::new() und dewa
Also kürzerer Code ist nicht immer lesbarer.

Nehmen wir die Methode zum Lesen der Daten aus der Datenbank: Wenn ich die aufrufe, dann muss ich gar nicht wissen, wie sie genau arbeitet. Ich habe aber eine Vorstellung davon, was sie tut. Und wenn ich es genau wissen will, schaue ich den Code an. Dennoch bedeutet eine Methode immer ein paar Zeilen Overhead.

Kürzerer Text ist auch nicht zwingend verständlicher. Zudem gelten für Text - speziell für Meldungen (in Medien) - mitunter Regeln, die nicht der Leser vorgibt. Bei Print-Zeitungen/Zeitschriften gilt z.B., dass das wichtige am Anfang kommen muss, weil am Ende potentiell der Text gekürzt wird. D.h. das Layout wirkt sich mitunter auf den Text aus.

Ich finde Code sollte ausdrucksstark sein, und ich finde solchen Code mittlerweile wirklich schlecht, sehr vereinfacht dargestellt, aber ich treffe ihn sehr sehr häufig an:
Code:
if (isFallA) {
  result.setErgebnis("ich bin Fall A");
} else {
  String fall = readFall();
  result.setErgebnis("ich bin ein anderer Fall: " + fall);
}
Was stört mich hier?

Die Aufgabe dieses Codes ist es, result.setErgebnis aufzurufen. Was ins Auge sticht ist aber das "if". Schöner finde es das zu machen:
Code:
result.setErgebnis(determineErgebis(...));
Mag Overhead sein, und je nach Situation kann ich auch mit einem tenären Operator leben.

Wenn in den einzelnen if-Zweigen mehr gemacht wird, sieht es wieder ganz anders aus, mir geht es hier wirklich nur um die Situation, bei der in allen Fällen genau eine Sache mit unterschiedlichen Parametern gemacht werden soll (das Zusammenbauen/Bestimmen der Parameter gehört für mich da dazu).
 
  • Gefällt mir
Reaktionen: CyborgBeta und SuperCube
So, einmal tief durchatmen ... und inzwischen sieht es so aus und ist "schön" (oder "schöner" als vorher ...): :D

Java:
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.json.JSONArray;
import org.json.JSONObject;

public class Bina {
  public record Tables(List<Table> tables) {}

  public record Table(List<Row> rows) {}

  public record Row(Date at, String asset, Type type, double[] doubleValues) {}

  public record FormattedTables(List<FormattedTable> tables) {}

  public record FormattedTable(List<FormattedRow> rows) {}

  public record FormattedRow(Date at, String asset, Type type, String[] doubleValues) {}

  enum Type {
    base,
    normal,
    hide,
    ignore,
    sum
  }

  //private static final Set<String> bases = Set.of("FDUSD");
  //private static final Set<String> toIgnore = Set.of("EUR", "ETHW", "NFT");
  //private static final Set<String> toHide = Set.of("BNB", "PEPE");

  public static void sortTables(Tables tables) {
    for (Table t : tables.tables()) {
      t.rows().sort(Comparator.comparing(Row::at));
      t.rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[8]));
    }
    tables.tables().sort(Comparator.comparing(t -> t.rows().get(0).at()));
  }

  public static void sortTable(Table table) {
    table.rows().sort(Comparator.comparing(Row::at));
    table.rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[8]));
  }

  public static Tables splitLargeTable(Table table) {
    sortTable(table);
    return new Tables(
        table.rows().stream()
            .collect(
                Collectors.groupingBy(
                    Row::at, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)))
            .values()
            .stream()
            .map(Table::new)
            .collect(Collectors.toCollection(ArrayList::new)));
  }

  public static FormattedTables getAllData() throws Exception {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

    List<List<Object>> data = MDB.getBina();
    Tables tables =
        splitLargeTable(
            new Table(
                data.stream()
                    .map(
                        row ->
                            new Row(
                                new Date(((Timestamp) row.get(0)).getTime()),
                                (String) row.get(1),
                                Type.valueOf((String) row.get(2)),
                                IntStream.range(3, 10)
                                    .mapToDouble(i -> (double) row.get(i))
                                    .toArray()))
                    .collect(Collectors.toCollection(ArrayList::new))));
    sortTables(tables);
    tables
        .tables()
        .forEach(
            table ->
                table
                    .rows()
                    .add(
                        new Row(
                            table.rows().get(0).at(),
                            "---",
                            Type.sum,
                            IntStream.range(3, 10)
                                .mapToDouble(
                                    i ->
                                        table.rows().stream()
                                            .mapToDouble(r -> r.doubleValues()[i])
                                            .sum())
                                .toArray())));
    return convert(tables);
  }

  public static FormattedTables convert(Tables tables) {
    return new FormattedTables(
        tables.tables().stream()
            .map(
                table ->
                    new FormattedTable(
                        table.rows().stream()
                            .map(
                                row ->
                                    new FormattedRow(
                                        row.at(),
                                        row.asset(),
                                        row.type(),
                                        Arrays.stream(row.doubleValues())
                                            .mapToObj(d -> String.format("%.2f", d))
                                            .toArray(String[]::new)))
                            .collect(Collectors.toCollection(ArrayList::new))))
            .collect(Collectors.toCollection(ArrayList::new)));
  }
}

Das Argument, "das kann ich aber nicht mehr lesen ...", zählt nicht. ;)
 
Was ich wohl definitiv machen würde:
Die ganzen magic numbers als Spalten würde ich als Konstanten definieren. Also was bedeutet 8 z.B.?#

Mich stört direkt auch die Redundanz 37,38 & 44,45: Warum ruft sortTables nicht - wie es die meisten wohl erwarten würden - für jede Table sortTable auf?
 
Ja, stimmt, das könnte man so machen:

Java:
public class Bina {
  public record Tables(List<Table> tables) {}

  public record Table(List<Row> rows) {}

  public record Row(Date at, String asset, Type type, double[] doubleValues) {}

  public record FormattedTables(List<FormattedTable> tables) {}

  public record FormattedTable(List<FormattedRow> rows) {}

  public record FormattedRow(Date at, String asset, Type type, String[] doubleValues) {}

  public enum Type {
    base,
    normal,
    hide,
    ignore,
    sum
  }

  private static final int sum1 = 8;

  public static void sortTables(Tables tables) {
    tables.tables().sort(Comparator.comparing(t -> t.rows().get(0).at()));
  }

  public static void sortTable(Table table) {
    table.rows().sort(Comparator.comparing(Row::at));
    table.rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[sum1]));
  }

  public static Tables splitLargeTable(Table table) {
    sortTable(table);
    return new Tables(
        table.rows().stream()
            .collect(
                Collectors.groupingBy(
                    Row::at, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)))
            .values()
            .stream()
            .map(Table::new)
            .collect(Collectors.toCollection(ArrayList::new)));
  }

  public static FormattedTables getAllData() throws Exception {
    //    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //    df.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

    List<List<Object>> data = MDB.getBina();
    Tables tables =
        splitLargeTable(
            new Table(
                data.stream()
                    .map(
                        row ->
                            new Row(
                                new Date(((Timestamp) row.get(0)).getTime()),
                                (String) row.get(1),
                                Type.valueOf((String) row.get(2)),
                                IntStream.range(3, 10)
                                    .mapToDouble(i -> (double) row.get(i))
                                    .toArray()))
                    .collect(Collectors.toCollection(ArrayList::new))));
    sortTables(tables);
    tables
        .tables()
        .forEach(
            table ->
                table
                    .rows()
                    .add(
                        new Row(
                            table.rows().get(0).at(),
                            "---",
                            Type.sum,
                            IntStream.range(0, sum1)
                                .mapToDouble(
                                    i ->
                                        table.rows().stream()
                                            .mapToDouble(r -> r.doubleValues()[i])
                                            .sum())
                                .toArray())));
    return convert(tables);
  }

  public static FormattedTables convert(Tables tables) {
    return new FormattedTables(
        tables.tables().stream()
            .map(
                table ->
                    new FormattedTable(
                        table.rows().stream()
                            .map(
                                row ->
                                    new FormattedRow(
                                        row.at(),
                                        row.asset(),
                                        row.type(),
                                        Arrays.stream(row.doubleValues())
                                            .mapToObj(d -> String.format("%.2f", d))
                                            .toArray(String[]::new)))
                            .collect(Collectors.toCollection(ArrayList::new))))
            .collect(Collectors.toCollection(ArrayList::new)));
  }
}

Aber das Formatieren des Datums fehlt auch noch und ich habe es bislang nicht getestet.

Ein zweites Problem ist: Die Listen sind alle mutable/sortierbar, man sollte aus einer Methode aber nur immutable Listen heraus geben, wenn man Records verwendet.
 
Also die generische Lösung rund um "double[] doubleValues" bringt aus meiner Sicht viel mehr Nachteile als Vorteile.
Am Ende sparst du dir nur ein paar Zeilen einfachen Code, bekommst aber etwas schwer Verständliches/Wartbares und hantierst mir harten Listindizes herum (die man ggf. nachziehen muss, wenn sich etwas an der Datenbanktabelle ändert). Der Typ "Row" ist in der Form dann vermutlich nur sehr eingeschränkt für deinen Einsatzzweck zu gebrauchen (anstatt einen global verwendbaren Typ zu haben der der Datenbanktabelle entspricht).

Wenn generisch dann rate ich eher zu einer Map: Feldname zu Feldwert. Damit bist du viel sicherer gegenüber Änderungen an der Datenbanktabelle, außerdem ist ein Feldname verständlicher als ein Listindex.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: CyborgBeta
@Wo bin ich hier Stimmt. Aber eine Map hat den Nachteil, dass alle Values vom gleichen Typ sein müssen. Also entfiele die "Typsicherheit" an dieser Stelle. Das ist unschön.

List<Double> wäre vielleicht günstiger.
 
Gleichzeitig hat die Map wieder andere Nachteile...

Ich würde das Array vermutlich als eine "IrgendwasValues"-Klasse zusammenfassen, intern vermutlich ein Array. Sehe keine Vorteile für eine List. (Irgendwas, weil ich jetzt nicht genau darüber nachgedacht habe, was für Beträge da genau drin sind)

Und die hat dann entsprechende Getter auf die einzelnen Werte. Die Summe aus zwei Instanzen kann diese Klasse logischerweise auch bilden.

Wobei das alles auch sehr stark von der Nutzung abhängt. Und wie gesagt, vermutlich würde ich auch schon bei der Modellierung der Datenbank ansetzen.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: CyborgBeta
Ok, das ist zwar noch nicht getestet, aber wäre das besser?

Java:
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.json.JSONArray;
import org.json.JSONObject;

public class Bina {
  public record FormattedTables(List<FormattedTable> tables) {}

  public record FormattedTable(List<FormattedRow> rows) {}

  public record FormattedRow(
      String at,
      String asset,
      String type,
      String free,
      String locked,
      String sum,
      String free_usd,
      String locked_sum,
      String sum1_usd,
      String sum2_usd) {}

  public enum Type {
    base,
    normal,
    hide,
    ignore,
    sum
  }

  private record Tables(List<Table> tables) {
    void sort() {
      tables().forEach(Table::sort);
      tables().sort(Comparator.comparing(t -> t.rows().get(0).at()));
    }
  }

  private record Table(List<Row> rows) {
    void sort() {
      rows().sort(Comparator.comparing(Row::at));
      rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[sum1]));
    }
  }

  private record Row(Date at, String asset, Type type, double[] doubleValues) {}

  private static final int sum1 = 8;

  private static Tables splitLargeTable(Table table) {
    table.sort();
    return new Tables(
        table.rows().stream()
            .collect(
                Collectors.groupingBy(
                    Row::at, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)))
            .values()
            .stream()
            .map(Table::new)
            .collect(Collectors.toCollection(ArrayList::new)));
  }

  private static Tables getAllData() throws Exception {
    List<List<Object>> data = MDB.getBina();
    Tables tables =
        splitLargeTable(
            new Table(
                data.stream()
                    .map(
                        row ->
                            new Row(
                                new Date(((Timestamp) row.get(0)).getTime()),
                                (String) row.get(1),
                                Type.valueOf((String) row.get(2)),
                                IntStream.range(3, 10)
                                    .mapToDouble(i -> (double) row.get(i))
                                    .toArray()))
                    .collect(Collectors.toCollection(ArrayList::new))));
    tables.sort();
    tables
        .tables()
        .forEach(
            table ->
                table
                    .rows()
                    .add(
                        new Row(
                            table.rows().get(0).at(),
                            "---",
                            Type.sum,
                            IntStream.range(0, sum1)
                                .mapToDouble(
                                    i ->
                                        table.rows().stream()
                                            .mapToDouble(r -> r.doubleValues()[i])
                                            .sum())
                                .toArray())));
    return tables;
  }

  public static FormattedTables getAllDataUnmodifiable() throws Exception {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
    String formatter = "%.2f";
    return new FormattedTables(
        getAllData().tables().stream()
            .map(
                table ->
                    new FormattedTable(
                        table.rows().stream()
                            .map(
                                row ->
                                    new FormattedRow(
                                        df.format(row.at()),
                                        row.asset(),
                                        row.type().name(),
                                        String.format(formatter, row.doubleValues()[0]),
                                        String.format(formatter, row.doubleValues()[1]),
                                        String.format(formatter, row.doubleValues()[2]),
                                        String.format(formatter, row.doubleValues()[3]),
                                        String.format(formatter, row.doubleValues()[4]),
                                        String.format(formatter, row.doubleValues()[5]),
                                        String.format(formatter, row.doubleValues()[6])))
                            .toList()))
            .toList());
  }
}

Es gibt aber eine (zwei) Sache(n), an der sich SpotBugs hier stört:

Code:
> Task :spotbugsMain
M V EI: Bina$FormattedTables.tables() may expose internal representation by returning tables  At Bina.java:[line 11]
M V EI2: new Bina$FormattedTables(List) may expose internal representation by storing an externally mutable object into tables  At Bina.java:[line 11]
M V EI: Bina$FormattedTable.rows() may expose internal representation by returning rows  At Bina.java:[line 13]
M V EI2: new Bina$FormattedTable(List) may expose internal representation by storing an externally mutable object into rows  At Bina.java:[line 13 ]

Das kann ich aber nicht nachvollziehen, da .toList() bereits eine unmodifizierbare Collection zurückgibt.

Durch eine kurze Recherche im Internet habe ich noch keine Lösung gefunden.
 
In Zeile 23 ist ein Typo 😅
 
Ich halte von den Listen-Wrapper-Records eher wenig.
Wenn du eine unmodifierbare Liste haben willst, gibt es auch wege, das ausdrucksstärker zu machen, also auch im Code. Das ".toList()" macht eine immutable List, ja aber die Implementierung zeigt nicht, ob das "Absicht" ist oder "Zufall".
Da ich eh ein Fan von Apache Commons bin, würde ich mir überlegen, ob ich Tables als Interface definiere:
public interface Tables extends List<Table>, Unmodifiable
und dann via Apache Commons eben explizit eine Unmodifiable List zurückliefern, und nicht implizit wie über den Stream.

Und das hier geht dann etwas in Richtung "Spinnerei":
Man könnte das ganze Typen-mäßig dann noch generischer machen zu irgendwie sowas
Code:
public interface Tables<T extends Table<R>, R extends TableRow> extends List<T>, Unmodifiable {
};
public interface Table<R extends TableRow> extends List<R>, Unmodifiable {
};
public interface TableRow {  // ob interface, Klasse, Record sei an der Stelle mal dahingestellt, geht eher um die Listen
};
Der Typ für FormattedTables würde so aussehen:
Code:
public interface FormattedTables extends Tables<FormattedTable, FormattedTableRow> {
};
public interface FormattedTable extends Table<FormattedTableRow> {
};
public interface FormattedTableRow extends TableRow {
};

Aber da muss man sich dann am Ende wirklich fragen: Ist das dann nicht schon Overengineering?
 
  • Gefällt mir
Reaktionen: CyborgBeta
Das, was du gesagt hast, ist richtig, eine generische(rere) Lösung ist super, wird aber vielleicht gar nicht vom Problem verlangt unbedingt ... Ich habe noch eine Idee, womit SpotBugs auch glücklich ist (grenzt aber auch fast schon an Overengineering):

Java:
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONObject;

public class Bina {
  public static class FormattedRowsSupplier implements Iterable<List<List<String>>> {
    private final FormattedTables tables;

    private FormattedRowsSupplier(FormattedTables tables) {
      this.tables = tables;
    }

    @NotNull
    @Override
    public Iterator<List<List<String>>> iterator() {
      return new Iterator<>() {
        private int currentIndex = 0;

        @Override
        public boolean hasNext() {
          return currentIndex < tables.tables().size();
        }

        @Override
        public List<List<String>> next() {
          List<List<String>> result = new ArrayList<>();
          for (FormattedRow fr : tables.tables().get(currentIndex).rows()) {
            result.add(
                List.of(
                    fr.at(),
                    fr.asset(),
                    fr.type(),
                    fr.free(),
                    fr.locked(),
                    fr.sum(),
                    fr.free_usd(),
                    fr.locked_usd(),
                    fr.sum1_usd(),
                    fr.sum2_usd()));
          }
          currentIndex++;
          return result;
        }
      };
    }
  }

  private record FormattedTables(List<FormattedTable> tables) {}

  private record FormattedTable(List<FormattedRow> rows) {}

  private record FormattedRow(
      String at,
      String asset,
      String type,
      String free,
      String locked,
      String sum,
      String free_usd,
      String locked_usd,
      String sum1_usd,
      String sum2_usd) {}

  private enum Type {
    base,
    normal,
    hide,
    ignore,
    sum
  }

  private record Tables(List<Table> tables) {
    void sort() {
      tables().forEach(Table::sort);
      tables().sort(Comparator.comparing(t -> t.rows().get(0).at()));
    }
  }

  private record Table(List<Row> rows) {
    void sort() {
      rows().sort(Comparator.comparing(Row::at));
      rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[sum1]));
    }
  }

  private record Row(Date at, String asset, Type type, double[] doubleValues) {}

  private static final int sum1 = 8;

  private static Tables splitLargeTable(Table table) {
    table.sort();
    return new Tables(
        table.rows().stream()
            .collect(
                Collectors.groupingBy(
                    Row::at, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)))
            .values()
            .stream()
            .map(Table::new)
            .collect(Collectors.toCollection(ArrayList::new)));
  }

  private static Tables getAllData() throws Exception {
    List<List<Object>> data = MDB.getBina();
    Tables tables =
        splitLargeTable(
            new Table(
                data.stream()
                    .map(
                        row ->
                            new Row(
                                new Date(((Timestamp) row.get(0)).getTime()),
                                (String) row.get(1),
                                Type.valueOf((String) row.get(2)),
                                IntStream.range(3, 10)
                                    .mapToDouble(i -> (double) row.get(i))
                                    .toArray()))
                    .collect(Collectors.toCollection(ArrayList::new))));
    tables.sort();
    tables
        .tables()
        .forEach(
            table ->
                table
                    .rows()
                    .add(
                        new Row(
                            table.rows().get(0).at(),
                            "---",
                            Type.sum,
                            IntStream.range(0, sum1)
                                .mapToDouble(
                                    i ->
                                        table.rows().stream()
                                            .mapToDouble(r -> r.doubleValues()[i])
                                            .sum())
                                .toArray())));
    return tables;
  }

  public static FormattedRowsSupplier getAllDataUnmodifiable() throws Exception {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
    String formatter = "%.2f";
    return new FormattedRowsSupplier(
        new FormattedTables(
            getAllData().tables().stream()
                .map(
                    table ->
                        new FormattedTable(
                            table.rows().stream()
                                .map(
                                    row ->
                                        new FormattedRow(
                                            df.format(row.at()),
                                            row.asset(),
                                            row.type().name(),
                                            String.format(formatter, row.doubleValues()[0]),
                                            String.format(formatter, row.doubleValues()[1]),
                                            String.format(formatter, row.doubleValues()[2]),
                                            String.format(formatter, row.doubleValues()[3]),
                                            String.format(formatter, row.doubleValues()[4]),
                                            String.format(formatter, row.doubleValues()[5]),
                                            String.format(formatter, row.doubleValues()[6])))
                                .toList()))
                .toList()));
  }

Das werde ich gleich mal testen ...
 
Das hat geklappt. Es fehlte lediglich noch ein zusätzliches sort() in Zeile 93 und die double-Indices stimmten noch nicht (Zeile 99 und 100).

Man könnte sich noch überlegen, ob man FormattedRow (Zeile 62) wirklich braucht (die String-Attribute), oder ob dort nicht einfach ein String-Array sinnvoller wäre.

Schlussendlich habe ich:

Java:
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Bina {
  private static final Logger log = LoggerFactory.getLogger(Bina.class);

  public static class FormattedRowsSupplier implements Iterable<List<List<String>>> {
    private final FormattedTables tables;

    private FormattedRowsSupplier(FormattedTables tables) {
      this.tables = tables;
    }

    @NotNull
    @Override
    public Iterator<List<List<String>>> iterator() {
      return new Iterator<>() {
        private int currentIndex = 0;

        @Override
        public boolean hasNext() {
          return currentIndex < tables.tables().size();
        }

        @Override
        public List<List<String>> next() {
          List<List<String>> result = new ArrayList<>();
          for (FormattedRow fr : tables.tables().get(currentIndex).rows()) {
            result.add(
                List.of(
                    fr.at(),
                    fr.asset(),
                    fr.type(),
                    fr.free(),
                    fr.locked(),
                    fr.sum(),
                    fr.free_usd(),
                    fr.locked_usd(),
                    fr.sum1_usd(),
                    fr.sum2_usd()));
          }
          currentIndex++;
          return result;
        }
      };
    }
  }

  private record FormattedTables(List<FormattedTable> tables) {}

  private record FormattedTable(List<FormattedRow> rows) {}

  private record FormattedRow(
      String at,
      String asset,
      String type,
      String free,
      String locked,
      String sum,
      String free_usd,
      String locked_usd,
      String sum1_usd,
      String sum2_usd) {}

  private enum Type {
    base,
    normal,
    hide,
    ignore,
    sum
  }

  private record Tables(List<Table> tables) {
    void sort() {
      tables().forEach(Table::sort);
      tables().sort(Comparator.comparing(t -> t.rows().get(0).at()));
    }
  }

  private record Table(List<Row> rows) {
    void sort() {
      rows().sort(Comparator.comparing(Row::at));
      rows().sort(Comparator.comparingDouble(r -> r.doubleValues()[sum2_usd]));
      rows().sort(Comparator.comparing(Row::type));
    }
  }

  private record Row(Date at, String asset, Type type, double[] doubleValues) {}

  private static final int numberOfDoubles = 7;
  private static final int sum2_usd = 6;

  private static Tables splitLargeTable(Table table) {
    table.sort();
    return new Tables(
        table.rows().stream()
            .collect(
                Collectors.groupingBy(
                    Row::at, LinkedHashMap::new, Collectors.toCollection(ArrayList::new)))
            .values()
            .stream()
            .map(Table::new)
            .collect(Collectors.toCollection(ArrayList::new)));
  }

  private static Tables getAllData() throws Exception {
    List<List<Object>> data = MDB.getBina();
    Tables tables =
        splitLargeTable(
            new Table(
                data.stream()
                    .map(
                        row ->
                            new Row(
                                new Date(((Timestamp) row.get(0)).getTime()),
                                (String) row.get(1),
                                Type.valueOf((String) row.get(2)),
                                IntStream.range(3, row.size())
                                    .mapToDouble(i -> (double) row.get(i))
                                    .toArray()))
                    .collect(Collectors.toCollection(ArrayList::new))));
    tables.sort();
    tables
        .tables()
        .forEach(
            table ->
                table
                    .rows()
                    .add(
                        new Row(
                            table.rows().get(0).at(),
                            "---",
                            Type.sum,
                            IntStream.range(0, numberOfDoubles)
                                .mapToDouble(
                                    i ->
                                        table.rows().stream()
                                            .mapToDouble(r -> r.doubleValues()[i])
                                            .sum())
                                .toArray())));
    return tables;
  }

  public static FormattedRowsSupplier getAllDataUnmodifiable() throws Exception {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
    String formatter = "%.2f";
    return new FormattedRowsSupplier(
        new FormattedTables(
            getAllData().tables().stream()
                .map(
                    table ->
                        new FormattedTable(
                            table.rows().stream()
                                .map(
                                    row ->
                                        new FormattedRow(
                                            df.format(row.at()),
                                            row.asset(),
                                            row.type().name(),
                                            String.format(formatter, row.doubleValues()[0]),
                                            String.format(formatter, row.doubleValues()[1]),
                                            String.format(formatter, row.doubleValues()[2]),
                                            String.format(formatter, row.doubleValues()[3]),
                                            String.format(formatter, row.doubleValues()[4]),
                                            String.format(formatter, row.doubleValues()[5]),
                                            String.format(formatter, row.doubleValues()[6])))
                                .toList()))
                .toList()));
  }
}

Danke an alle hier Helfenden.
 
Zuletzt bearbeitet:
Offtopic: kann weg
 
Zuletzt bearbeitet:
Zurück
Oben