Wie kann man den RowSorter dazu bringen die ursprünglich liste wieder anzuzeigen.

richis-fragen

Aktives Mitglied
Guten Abend
habe einen TableRowSorter der JTable hinzugefügt.
Macht auch: sortierne A-Z und Z-A!

Im TableModel ist ja noch die ursprünglich Reihenfolge. Auch das ist Gut!

Wie, resp. Was muss ich dem RowSorter mitteilen, dass er wieder die Ursprünglich Liste anzeigt?
Wollte das mit rechts-Klick auf den Spaltenkopf realisieren. (TableHeader.addMouseListener etc....)

Rechtsklick funktioniert ... nur wie / was muss ich hier dem RowSorter übergeben, dass er mir wieder das urspüngliche TableModel läd?

Habe das Forum und den Google bemüht (wahrscheinlich false Fragestellung gemacht) aber ich habe nichts gefunden.
Wenn einmal der Spaltenkopf zum sortieren geklickt wurde, gibt es nur noch den Wechsel zwischen A-Z oder Z-A, Ursprunganzeige geht nicht mehr.

Wie könnte die Sortierung wieder angezeigt werden wie sie beim befüllen war?

Angehängt habe ich so:
Java:
        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>;
        table.setRowSorter(sorter);
        sorter.setModel(DefaultTableModel);

Vielen Dank
Richi
 

KonradN

Super-Moderator
Mitarbeiter
Da meine Nacht etwas früher vorbei war und ich Zeit totschlagen musste:

Betrachten wir einmal die Fragestellung, wie man sowas denn dann umsetzen kann. Machen wir erst einmal eine einfache Lösung:
Wir wollen, dass bei einem Klick auf eine Überschrift gewechselt wird zwischen: Aufsteigend, absteigend und unsortiert.

Das Verhalten diesbezüglich macht im TableRowSorter die Methode toggleSortOrder - also überschreiben wir das einfach einmal um dies zu implementieren:
Java:
import javax.swing.*;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.util.ArrayList;
import java.util.List;

class MyTableRowSorter<M extends TableModel> extends TableRowSorter<M> {
    private boolean ascending = true;

    public MyTableRowSorter(M model) {
        super(model);
    }

    @Override
    public void toggleSortOrder(int column) {
        List<SortKey> sortKeys = new ArrayList<>(getSortKeys());
        if (sortKeys.isEmpty() || sortKeys.get(0).getColumn() != column) {
            sortKeys.clear();
            sortKeys.add(new SortKey(column, SortOrder.ASCENDING));
            ascending = true;
        } else if (ascending) {
            sortKeys.set(0, new SortKey(column, SortOrder.DESCENDING));
            ascending = false;
        } else {
            sortKeys.clear();
            ascending = true;
        }
        setSortKeys(sortKeys);
    }
}

Das wäre dann eine einfache Lösung, die man direkt einbinden kann mit einer einfachen Änderung:
TableRowSorter<TableModel> sorter = new MyTableRowSorter<TableModel>;
 

richis-fragen

Aktives Mitglied
Sorry,
Problem gelösst!
stimmt leider nicht ganz.... :confused:

Die Betrag-Spalte ist als Double.class definiert, aber sobald ich zwecks besserer Lesbarkeit die Beträge mit Tausender Trennzeichen versehe, werden die da wohl zu einem String.

Könnte man da noch eine CompareTo einbauen, der je nach Numberformat (CH / DE / usw.) korrekt sortiert?

Um die Performance zu testen, befeuere ich die Tabelle mit 1'000'000 (1 Mio) Records. (geladen » 0:668)
  • Sortiert (A-Z) » 2:234 sec.
  • Sortiert (Z-A) » 1:964 sec.
  • Sortiert (default) » 0:014 sec.
Das finde ich einfach SUUPER (Wer arbeitet schon mit 1Mio, Datensätze in einer Anwendung. Die kommen meistens gefiltert daher.)

Habe das mal ausprobiert:
Code:
  private int id;
  private String firstName;
  private String lastName;

  public Customer(int id, String firstName, String lastName) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
  }
Java:
@Override
public int compareTo(Customer o) {
  return Integer.compare(this.id, o.id);
}
Code:
Arrays.sort(customers);
System.out.println(Arrays.toString(customers));

In der Konsole wirft er mir die richtige Sortierung.
Sobald ich aber die erste Spalte mit Tausender-Trennzeichen (new DecimalFormat ("#,##0");) ändere, funktioniert es wieder nicht.
Aber wie krieg ich das jetzt in den RowSorter?

Vielen Dank für die Geduld.
Richi
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Damit er wirklich die double Werte zur Sortierung verwendet, musst Du einfach einen entsprechenden Comparator angeben. Dazu kannst Du auf dem TableRowSorter setComparator aufrufen. Bei double Werten in der Spalte mit Index 2 wäre das z.B. ein:

sorter.setComparator(2, (Comparator<Double>) Double::compareTo); (Methodenreferenz) oder
sorter.setComparator(2, (Comparator<Double>) (d1, d2) -> d1.compareTo(d2)); (Lambda Expression)

Oder wenn Du es lieber ausführlicher haben willst mit innerer Klasse:
Java:
        sorter.setComparator(2, new Comparator<Double>() {
            public int compare(Double d1, Double d2) {
                return d1.compareTo(d2);
            }
        });

Ein einfaches Testprogramm wäre dann:
Java:
import javax.swing.*;
import javax.swing.table.*;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame("JTable Sortierung entfernen Beispiel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Object[][] data = {
                {"John", "Doe", 0.01 },
                {"Alice", "Smith", 0.05 },
                {"Alice", "Johnson", 1000000.02 },
                {"Bob", "Johnson", 2000.04 },
                {"Jane", "Doe", 0.03 }
        };

        String[] columns = {"Vorname", "Nachname", "Double Value"};

        System.out.println(data[2][2].getClass().getName());

        TableModel model = new DefaultTableModel(data, columns);
        JTable table = new JTable(model);

        TableColumn doubleColumn = table.getColumnModel().getColumn(2);
        doubleColumn.setCellRenderer(new DefaultTableCellRenderer(){
            @Override
            protected void setValue(Object value) {
                if (value instanceof Double) {
                    setText(String.format("%,.3f", (Double) value));
                } else {
                    super.setValue(value);
                }
            }
        });

        MyTableRowSorter<TableModel> sorter = new MyTableRowSorter<>(model);
        sorter.setComparator(2, (Comparator<Double>) Double::compareTo);

        table.setRowSorter(sorter);

        frame.add(new JScrollPane(table));

        frame.pack();
        frame.setVisible(true);
    }
}
Bildschirmfoto 2024-02-16 um 11.22.58.png

==> Die 1 von der Millionen kommt nicht vor der 2 von dem Tausenderwert.
 

richis-fragen

Aktives Mitglied
Danke @KonradN
Das erste Codeschnipsel (5 Zeilen) hat mir weiter geholfen.
Das zweite habe ich ausprobiert und es macht eigentlich das was ich wollte, aber ich konnte es nicht in meinem Programm umsetzten.

Ich poste mal wie ich es gelösst habe: (habe aber dazu noch eine Frage im Anschluss)
Java:
    class ColumnListener extends MouseAdapter {
        protected JTable table;
        boolean isSortAsc = true;
        int sortStatus = 0;
        int oldColumn = -1;
        
        public ColumnListener(JTable t) {
            table = t;
        }

        public void mouseClicked(MouseEvent e) {
            if(basisArr.size() > 0) {
                long startTime = new Date().getTime();
                TableColumnModel colModel = table.getColumnModel();
                int columnModelIndex = colModel.getColumnIndexAtX(e.getX());
                int modelIndex = colModel.getColumn(columnModelIndex).getModelIndex();

                if (modelIndex < 0) {
                    return;
                }
                //Wenn einen andere Spalte geklickt wird: sortStatus auf 0 Setzen = DESC-Sortierung
                if(modelIndex != oldColumn) {
                    oldColumn = modelIndex;
                    sortStatus = 0;
                }
                
                if(sortStatus == 0) {
                    isSortAsc = false;
                    sortStatus = 1;
                }
                else if(sortStatus == 1) {
                    isSortAsc = true;
                    sortStatus = 2;
                }
                else if(sortStatus == 2) {
                    //Original Array zurück laden und sortStatus auf 0 Setzen = DESC-Sortierung
                    //oldColumn auf -1 -> es wurde noch keine Spalte geglickt (Ursprung wiederherstellen)
                    sortStatus = 0;
                    oldColumn = -1;
                    removeAll();
                    for(Object[] o : [B]basisArr[/B]) {
                        addRow(o);
                    }
                    long endTime = new Date().getTime();
                    String mSec = new SimpleDateFormat("s:SSS").format(endTime-startTime);
                    int total = tabelle.totalRows();
                    System.out.println("\n🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️\nArray ("+ new DecimalFormat ("#,##0").format(total).toString()+" Zeilen) Spalte: "+modelIndex+" SORTIERT (asc = "+isSortAsc+") » "+mSec+"\n🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️\n");
                    return;
                }
                
                @SuppressWarnings("unchecked")
                ArrayList<Object[]> tmp_arr = (ArrayList<Object[]>) basisArr.clone();
                Collections.sort(tmp_arr, new MyComparator(isSortAsc, modelIndex));
                removeAll();
                for(Object[] o : tmp_arr) {
                    addRow(o);
                }
                long endTime = new Date().getTime();
                String mSec = new SimpleDateFormat("s:SSS").format(endTime-startTime);
                int total = tabelle.totalRows();
                System.out.println("\n🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️\nArray ("+ new DecimalFormat ("#,##0").format(total).toString()+" Zeilen) Spalte: "+modelIndex+" SORTIERT (asc = "+isSortAsc+") » "+mSec+"\n🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️🕑️\n");
            }
        }
    }

    class MyComparator implements Comparator<Object[]> {
        protected boolean isSortAsc;
        protected int isSortCol;

        public MyComparator( boolean sortAsc, int sortCol) {
            isSortAsc = sortAsc;
            isSortCol = sortCol;
        }

        public int compare(Object[] o1, Object[] o2) {
            int result = 0;
            if (o1[isSortCol] instanceof String) {
                result = ((String)o1[isSortCol]).compareTo((String)o2[isSortCol]);
            }
            else if (o1[isSortCol] instanceof Integer) {
                result = ((Integer)o1[isSortCol]).compareTo((Integer)o2[isSortCol]);
            }
            else if (o1[isSortCol] instanceof Double) {
                result = ((Double)o1[isSortCol]).compareTo((Double)o2[isSortCol]);
            }
            else if (o1[isSortCol] instanceof Date) {
                result = ((Date)o1[isSortCol]).compareTo((Date)o2[isSortCol]);
            }
            else if (o1[isSortCol] instanceof Boolean) {
                result = ((Boolean)o1[isSortCol]).compareTo((Boolean)o2[isSortCol]);
            }
            if (!isSortAsc) {
                result = -result;
            }
            return result;
        }
    }
Das ganze habe ich in mein TableModel gepackt und es funktioniert super schnell.
Das basisArr wird paralell zum TableModel befeuert und dient allein dazu um die ursprüngliche DB-SQL-Abfrage wieder zu laden.

Beim laden und sortieren bin ich immer unter einer Sekunde mit einer Mio. Records. Ich glaube das ist eine akzeptable Performance.

Das einzige Problem was ich habe ist: Der Pfeil der mir die Sortierung anzeigt ist bei gebrauch des obigen Codes nicht mehr vorhanden.
Wie, resp. wo wird dieser Pfeil bei einem TableRowSorter im Heder gesetzt? (Sicher eine banale Frage, aber nicht für Anfänger)

Das wäre super.
Richi
 

KonradN

Super-Moderator
Mitarbeiter
Ok, du willst also keinen TableRowSorter nutzen sondern Du sortierst einfach selbst.

Du kannst den Header selbst rendern. Das Rendern machen DefaultTableCellRenderer. Davon kannst Du eine Klasse ableiten und dann dort die paintComponent Methode überschreiben.

Also machen wir einfach mal eine Klasse, die sortierte Header rendern kann:
Java:
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.util.Objects;

public class SortableHeaderRenderer extends DefaultTableCellRenderer {
    public static final int NONE = 0;
    public static final int UP = 1;
    public static final int DOWN = 2;

    private int sortDirection = NONE;
    private String sortedColumnTitle = null;

    public SortableHeaderRenderer() {
        setHorizontalAlignment(SwingConstants.CENTER);
    }

    public void setSortDirection(int sortDirection, String columnTitle) {
        this.sortDirection = sortDirection;
        this.sortedColumnTitle = columnTitle;
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (sortedColumnTitle == null || sortedColumnTitle.isEmpty()) return; // No sorting specified

        int arrowSize = 8;
        int x = getWidth() - arrowSize - 3;
        int y = (getHeight() - arrowSize) / 2;

        System.out.println(getText());
        if (Objects.equals(getText(), sortedColumnTitle)) {
            if (sortDirection == UP) {
                Polygon arrow = new Polygon();
                arrow.addPoint(x, y + arrowSize);
                arrow.addPoint(x + arrowSize, y + arrowSize);
                arrow.addPoint(x + arrowSize / 2, y);
                g.setColor(Color.BLACK);
                ((Graphics2D) g).fill(arrow);
            } else if (sortDirection == DOWN) {
                Polygon arrow = new Polygon();
                arrow.addPoint(x, y);
                arrow.addPoint(x + arrowSize, y);
                arrow.addPoint(x + arrowSize / 2, y + arrowSize);
                g.setColor(Color.BLACK);
                ((Graphics2D) g).fill(arrow);
            }
        }
    }
}

Diesen Renderer können wir dann im JTable Header als default Renderer setzen:
table.getTableHeader().setDefaultRenderer(new SortableHeaderRenderer());

Dann kannst Du im Code setzen, welche Spalte sortiert ist:
Java:
        SortableHeaderRenderer headerRenderer = (SortableHeaderRenderer) table.getTableHeader().getDefaultRenderer();
        headerRenderer.setSortDirection(SortableHeaderRenderer.UP, "Nachname");
        table.getTableHeader().repaint();

Das wäre eine Möglichkeit, wie man das machen könnte.
 

richis-fragen

Aktives Mitglied
Vielen Dank
sorry war gerade ausser Haus, aber werde es jetzt mal versuchen, ob ich das hin kriege.
Ok, du willst also keinen TableRowSorter nutzen sondern Du sortierst einfach selbst.
...das war jetzt nicht eine Entscheidung. Hab es halt nur so hin gegriegt. :) :)

Melde mich auf jeden Fall nochmals wenn ich obiges umgesetzt habe.

Danke.
Richi
 

richis-fragen

Aktives Mitglied
Sorry, kleine Frage: was sind das für Objekte im Header?
  • JButtons
  • JToggleButtons
  • JLabels
  • etwas anderes?
Könnte man obigen Objekten nicht einfach ein Icon (ImageIcon) mitgeben?
Mit folgendem Code kann ich ja den Text ändern:
Java:
TableColumn column = colModel.getColumn(modelIndex);
 column.setHeaderValue("Hallo 1");
Sowas müsste doch, wenn es JButtons, JLabels sind mit einem Icon möglich sein oder nicht?
 

richis-fragen

Aktives Mitglied
@KonradN Vielen, vielen Dank für Ihre Geduld.
Jetzt ist das Problem wirklich GELÖSST!
Habe es als JAR Exportiert und in einem anderen Projekt importiert und es macht alles genauso, wie ich es mit Ihrer Hilfe Programmiert habe.
Sortierungspfeile UP, DOWN, NONE sind perfekt.

Performance im neuen Projekt:
  1. Array (1’000’000 Zeilen) geladen » 0:581
  2. Array (1’000’000 Zeilen) Spalte: Strasse SORTIERT (DOWN) » 0:770
  3. Array (1’000’000 Zeilen) Spalte: Strasse SORTIERT (UP) » 0:769
  4. Array (1’000’000 Zeilen) Spalte: Strasse SORTIERT (NONE) » 0:637
Nochmals vielen Dank.
Richi
 

richis-fragen

Aktives Mitglied
Sorry, ich bins wieder.
Habe gesehen, dass Sie Double format so gesetzt haben:
setText(String.format("%,.3f", (Double) value));
Gibt es irgendwo eine Tabelle / Liste welche Schreibweise für welches Format gilt?
Gibt es das auch für Date Objekte ((Date) value) ?

Habe festgestellt, dass obige Art der formatierung viel performanter ist als:
new DecimalFormat ("#,##0.00").format(Integer.parseInt(value.toString()));

Zumindest bei meinen Double-Werten.

Wäre schön wenn es so eine Liste/Tabelle gäbe, oder eine Info was mit % oder .3f gemacht wird.
Gefunden habe ich nichts. (Weiss auch nicht wie man danach sucht)

Vielen Dank
Richi
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Zu dem:
Integer.parseInt(value.toString())
hatte ich ja bereits geschrieben, dass es unnötig ist. Das kannst Du zu einem (int) value machen.

Statt
new DecimalFormat ("#,##0.00")
solltest Du die Erzeugung heraus ziehen, also in der Klasse eine static Konstante machen a.la.:
private static final DecimalFormal MY_NUMBER_FORMAT = new DecimalFormat ("#,##0.00");`
um dann am Ende an der Stelle nur noch
MY_NUMBER_FORMAT.format( (int) value );
stehen zu haben.

Damit solltest Du bezüglich Performance vermutlich keinen Unterschied mehr sehen. MY_NUMBER_FORMAT solltest Du natürlich so umbennen, das klar ist, was Du da formatierst oder was es für ein Format ist (a.la. TWO_DIGITS_FORMAT).

Bezüglich der Dokumentation war @LimDul schneller, so dass ich das nicht mehr heraus suchen muss :)
 

richis-fragen

Aktives Mitglied
Vielen Dank @KonradN und @LimDul

@LimDul
Wow, zum Glück bin ich Pensioniert... da hat man ev. Zeit das durch zu lesen... :cool:

@KonradN
Meine Tabelle sollte mal ein table.jar werden.

Kann mann dann in der Anwendung die dieses table.jar verwendet (importiret wird) diese static final Formate pro Spalte übergeben?

Es könnte ja sein, dass ich eine Integere Zahl mit zwei Kommastellen (macht zwar keinen Sinn) anzeigen möchte.

->Davon augegangen, dass die Werte aus einer CSV, TXT oder schlecht gepflegtem EXCEL kommen.
Wenn ich z.B im ersten Record eine 2 übergebe wird die als Integer interpretiert
Übergebe ich aber in dem nächsten Record in gleicher Spalte eine Zahl wie 1.1 wird sie als Double interpretiert. Dies wird dann korrekt angezeigt.

Es wäre doch Sinnvoll, dass dann auch die Integere Zahl mit gleichem Anzeigeformat daherkommt? Oder versuche ich da was völlig sinnloses?

Vielen Dank
 

richis-fragen

Aktives Mitglied
@KonradN
Sorry ich muss ja nur den Typ: z.B. MY_NUMBER_FORMAT übergeben der Renderer weiss ja dann was er machen muss....
Das passt jetzt.

Aber wenn ich obiges mit einem Date-Format mache, wirft es mir eine Exception.
 

KonradN

Super-Moderator
Mitarbeiter
Was genau hast du denn versucht und was für eine Exception bekommst Du? Du kannst natürlich auch eine entsprechende DateFormat Instanz für Date Instanzen vorhalten.

Generell musst Du halt immer schauen, was für einen Typ die Variable hat. Und dann kannst Du passende Formate verwenden.

DecimalFormat.format mit einem Parameter gibt es zwei Mal: einmal mit double und einmal mit long. Damit kannst Du da alles als Parameter angeben, was sich implizit in ein long oder double umwandeln lässt. Ein int Wert ist damit ok.

Aber natürlich: ein Date kann nicht implizit umgewandelt werden, daher kannst Du da kein Date angeben.
(Aber mit DateFormat.format kannst Du ein java.util.Date übergeben und formatieren.)
 

richis-fragen

Aktives Mitglied
Vielen Dank @KonradN
Aber natürlich: ein Date kann nicht implizit umgewandelt werden, daher kannst Du da kein Date angeben.
(Aber mit DateFormat.format kannst Du ein java.util.Date übergeben und formatieren.)
Die Datumspalte bekommte ein Date in der Form:
Mon Mar 26 00:00:00 CET 1959 was ein java.util.Date-Objekt ist.

Wenn es aus einer DB kommt stimmt es sogar.

Wenn es aus einer CSV, TXT oder schlecht gepflegtem EXCEL kommt wird es dem Calendar entsprechend übergeben.
Aufbereitet; String.split("\\.) Std, Min. Sek. werden dann mit 0 gesetzt.
calendar.set(1959, 3, 26, 0, 0, 0);
Date d = (Date) calendar.getTime();
-> gibt mir dann zurück:
Mon Mar 26 00:00:00 CET 1959 was auch ein java.util.Date-Objekt ist.

Das wird dann als Object dem Model übergeben.

Date d = (Date) value;
Wenn ich dann d mit new SimpleDateFormat("dd.MM.yyyy").format(d) zum AnzeigeString formatiere,
dauert das bei 1 Mio Datensätzen ca. 4-8 Sekunden je nach CPU

Darum mein Frage ob es da nicht ewas schnelleres gibt.

Die Exceptions werden geworfen, wenn der Renderer noch nicht fertig ist und der User eine Spalte
sortieren / breite anpassen oder verschieben will...
Habe es jetzt mit while(!true){} gelösst. -> KEINE Exceptions mehr... Weiterarbeiten geht dann erst wenn true.

Vielen Dank.
 
Zuletzt bearbeitet:

Oneixee5

Top Contributor
Format-Instanzen sollten niemals statisch sein oder in Konstanten gehalten werde. Sie sind nicht threadsave, was zu unvorhergesehen Ergebnissen führen kann. Insbesondere gilt das für Anwendungen mit UI, da hier meist mit mehreren Threads gearbeitet wird. Die korrekte Verwendung wäre mit ThreadLocal.

Java:
private final static ThreadLocal<SimpleDateFormat> FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));



try {
    ...                         
    String formatted = FORMAT.get().format(new Date());           
    ...               
} finally {
    FORMAT.remove();
}
Allerdings ist das recht unpraktisch, da es oft nicht so einfach ist und man möglicherweise nicht genau weiß wann ein Thread beendet wird. Wenn ein Thread beendet wird, dann muss auch ThreadLocal#remove(); aufgerufen werden, da ansonsten der Speicher von ThreadLocal nicht freigegeben wird. Man kann sich eine andere Art des synchronisierten Zugriffs überlegen oder man erzeugt neue Format-Instanzen.
 

KonradN

Super-Moderator
Mitarbeiter
Format-Instanzen sollten niemals statisch sein oder in Konstanten gehalten werde.
Ja, das ist natürlich richtig und von mir war das an der Stelle tatsächlich ein falscher Ratschlag, da ich das beim Schreiben der Antwort schlicht übersehen habe. Vielen Dank für die Korrektur und den guten Hinweis!

Edit: Habe meinen Beitrag noch um einen Hinweis zu Deinem Hinweis ergänzt! Der Fehler ist so gravierend, dass ich das für notwendig erachtet habe.
 

richis-fragen

Aktives Mitglied
Habe ich das jetzt richtig verstanden?
private final static ThreadLocal<SimpleDateFormat> FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
Deklariere ich in meinem CellRenderer (implemnets TableCellRenderer) ?!?

Den try:
try {
...
String formatted = FORMAT.get().format(new Date());
...
} finally {
FORMAT.remove();
}
innerhalb des public Component getTableCellRendererComponent(...) ?
Java:
if(value instanceof Date) {
  try{
     String formatted = FORMAT.get().format(new Date());
  } finally {
     FORMAT.remove();
  }
}

Habe ich gemacht, aber ich glaube das betrifft nur die aktuelle Zelle.
 

richis-fragen

Aktives Mitglied
Wenn ich auf den Spaltenkopf klicke um zu sortieren, die Spalte verschiebe/Grösse verändere, rennt er mir trotzdem in die Exception.

Wenn ich aber jetzt mit dem while arbeite, kann ich draufklicken und es wird erst sortiert, wenn der CellRenderer fertig ist,

auch wenn ich eine ZELLE doppelklicke um den Inhalt zu bearbeiten wird gewartet bis der Renderer fertig ist.

Die klicks / doppelklicks werden wie bei Java üblich angenommen aber eben erst ausgeführt wenn der Renderer fertig ist.

Klar rendert der gleich nochmals aber die klicks werden vorher abgearbeitet. Auch das Überschreiben geht, obwohl der Renderer gleich nach dem aktivieren der ZELLE nochmals loslegt.

Ich glaube das abfangen weiterer Aktivitäten hilft in meinem Fall am meisten.

Exceptions kommen nur wenn:
  • 1 Mio. Records das erste mal geladen werden.
  • Ein Spalte verschoben wird.
  • Sortiert wird.
und der User gleichzeitig irgendwas in/an der Tabelle händeln will.

Bin jetzt gerade dabei mit einem GlassPane zu testen, was dann auch über while(!true) läuft.


Im jetztigen Zustand wird, wenn nicht gerade Spalten verschoben / sortiert werden, wird die Tabelle in +/- 600 ms gefiltert wenn ich z.B. in ALLEN Spalten nach einem Buchstaben oder einer Zahl suche und das bei 1 Mio. Records.

Wenn ich das gleiche über den RowSorter (ohne meine SuchKlasse) mache brauche ich über 10 Sek.

Ich danke vielmals, aber ich glaube ich bleibe bei der Format-Übergabe via HashMap und dem while(!true). Bis jetzt ist es das schnellste was ich je Programmiert habe.

Vielen Dank für Eure Geduld.

Richi
 

KonradN

Super-Moderator
Mitarbeiter
Arbeitest du mit mehreren Threads? Die Idee hinter dem ThreadLocal ist ja, dass Du für jeden Thread eine Instanz erstellst um diese dann mehrfach zu verwenden.

Wenn Du die Instanz direkt nach der Nutzung entfernst, dann bringt das so nichts außer es eben komplizierter zu machen. Da ist es dann günstiger, wieder zu der direkten Erstellung zu wechseln.

Wenn Du weisst, wo das überall aufgerufen wird, dann kannst Du das nutzen. Wenn Du z.B. weisst, dass Du den Aufruf immer nur im UI Thread machst, dann kannst Du das FORMAT.remove() ganz weglassen.

Und auch noch der Hinweis: ThreadLocal nutzt "weak" References. Damit werden die Elemente auch dann entfernt, wenn der Thread, zu dem die Instanz zugeordnet wurde, komplett vom GC entfernt wurde. Darauf sollte man sich aber nicht verlassen. Da ist es dann besser, dass man die Formatter immer neu erstellt.
 

Oneixee5

Top Contributor
Es wäre auch möglich den Zugriff auf den Formatter zu synchronisieren. Das hat den Nachteil, das deine Anwendung vermutlich etwas gebremst wird. Synchronisierung kostet Zeit.
 

KonradN

Super-Moderator
Mitarbeiter
Wenn ich die Problematik richtig verstanden habe, wäre die saubere Lösung aus meiner Sicht, dass Du Aufgaben, die längere Zeit dauern können, in einen separaten Thread auslagerst. Das Laden erfolgt dann also im Hintergrund und erst wenn alles geladen wurde, dann aktualisierst Du die UI.

Bei den anderen zeitaufwendigen Operationen ist es prinzipiell ähnlich. Wobei die Frage ist, was da wirklich die Zeit kostet. Bereinigst Du beim Laden die Daten? Sprich: Wenn Du ein Datum als String bekommst: Dann parst Du diese einmalig und speicherst dann den Datensatz intern mit einem Date?

Und wenn die Formatierung immer im UI Thread erfolgt, dann kannst Du wie gesagt das remove() entfernen und dann hast Du da auch weniger Instanzen, die erzeugt werden (bei 1 Mio Datensätzen würde er ja sonst pro Spalte jeweils 1 Mio Formater erzeugen). Das ist dann sauberer wie das, was ich da teilweise (unsauber) mache mit dem private static.

Aber das behebt nicht das Problem sondern reduziert die Zeit nur, in denen ein Absturz möglich ist. Wirklich verhindern kann man das ggf nur, dass man den Status abfragt, ob er noch am rendern ist oder nicht (ich habe da jetzt gerade nicht den Überblick, wo und wie das ginge). Und dann kannst Du bei der Sortierung dies abfragen und die Sortierung einfach nicht machen, wenn das noch nicht fertig ist.
 

Oneixee5

Top Contributor
Man könnte sich auch endlich von java.util.Date und Calendar verabschieden und java.time.format.DateTimeFormatter verwenden:
Code:
Implementation Requirements:
    This class is immutable and thread-safe.
Since:
    1.8
Java:
  LocalDate date = LocalDate.now();
  DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
  String text = date.format(formatter);
  LocalDate parsedDate = LocalDate.parse(text, formatter);

Ach sorry, es ging nicht um Datum-Zeit sondern um Number
 

richis-fragen

Aktives Mitglied
@Oneixee5
Oh ja, das braucht Zeit und meine Such-Klasse kann die Synchronisierung nicht einfach so umsetzten ohne noch mehr Perfomance-Verlust.

@KonradN
Ja ich lagere diese Recourcenintensiven Prozesse in Threads aus... aber wenn ich keine waitstates einbaue, rennt er wieder in die Exception.
Wenn man das Programm nicht im Eclipse und nicht im Terminal/Eingabeaufforderung startet, sieht und merkt man die Exceptions nicht.

Wäre also alle kein Problem, wenn da nicht die unsicherheit bestehen würde, das am ende inkonsistenz der DB oder anderen Datenverlusts im realen Betrieb, entstehen könnten.

Aber so wie es jetzt läuft, kann ich mit sicherheit sagen, dass das nicht passiert.
Teste aber noch alle möglichen und Unmöglichen manipulationen eines Users.

@Oneixee5
Intressant... werde das morgen mal testen.
Java:
Implementation Requirements:
    This class is immutable and thread-safe.
Since:
    1.8

Java:

  LocalDate date = LocalDate.now();
  DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
  String text = date.format(formatter);
  LocalDate parsedDate = LocalDate.parse(text, formatter);
Vielen Dank
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N SWT Wie verwende ich den RowSorter? AWT, Swing, JavaFX & SWT 13
J Swing JTable RowSorter AWT, Swing, JavaFX & SWT 0
D Swing Letzte Zeile einer JTable nicht über RowSorter sotieren AWT, Swing, JavaFX & SWT 2
P Swing JXTable RowSorter AWT, Swing, JavaFX & SWT 12
C Swing RowSorter getSortOrder() ? AWT, Swing, JavaFX & SWT 2
J Swing RowSorter für (negative) Integer / Floats - JTable AWT, Swing, JavaFX & SWT 5
bluerob Nach RowSorter -> Keine Tabellenüberschreibung möglich AWT, Swing, JavaFX & SWT 7
E Eigenen RowSorter AWT, Swing, JavaFX & SWT 11
T JTable / RowSorter macht große Probleme AWT, Swing, JavaFX & SWT 2
hdi Swing JTable: Fehler beim Nutzen von RowSorter#setRowFilter AWT, Swing, JavaFX & SWT 4
hdi Swing JTable: Concurrency fireTableDataChanged() + RowSorter? AWT, Swing, JavaFX & SWT 5
S Swing JTable RowSorter trotz RowFilter abschalten? AWT, Swing, JavaFX & SWT 2
C JTable mit RowSorter und Drag & Drop: Zeile verschieben AWT, Swing, JavaFX & SWT 4
Daniel_L JTable, (Auto-)RowSorter und NullPointer-Exception AWT, Swing, JavaFX & SWT 6
V Vector->TableModel->RowSorter:Bei Markierung Index im AWT, Swing, JavaFX & SWT 2
G Problem mit RowSorter AWT, Swing, JavaFX & SWT 18
W Kennt jemand Dear ImGui (und den Java-Wrapper dazu)? AWT, Swing, JavaFX & SWT 0
Y JavaFX Bild speichern und Pfad dazu abspeichern AWT, Swing, JavaFX & SWT 2
D Neues Frame öffnen - und dazu altes schließen AWT, Swing, JavaFX & SWT 5
L unbestimmt viele JComboBox und ItemListener dazu erstellen AWT, Swing, JavaFX & SWT 2
A Problem Spiel auf Panel der GUI zu bringen AWT, Swing, JavaFX & SWT 1
N JavaFX applikation auf anderen Systemen zum laufen bringen AWT, Swing, JavaFX & SWT 7
K Array von einer Action zur anderen bringen AWT, Swing, JavaFX & SWT 7
O Swing Jpanel autom. auf JFrame Größe bringen AWT, Swing, JavaFX & SWT 6
S Elemente im Panel in richtige Ausgangsstellung bringen AWT, Swing, JavaFX & SWT 10
S Programm auf aktuellen Stand bringen AWT, Swing, JavaFX & SWT 2
F Swing Mehrere Textfelder in Scrollpane einfügen und dann zum Scrollen bringen? AWT, Swing, JavaFX & SWT 4
Nadja Text zum Leuchten Bringen AWT, Swing, JavaFX & SWT 2
E JList in ScrollPane auf größe bringen AWT, Swing, JavaFX & SWT 5
F HTML Text irgendwie auf BufferedImage bringen AWT, Swing, JavaFX & SWT 3
spacegaier BoxLayout: Buttons auf gleiche Größe bringen AWT, Swing, JavaFX & SWT 4
B SWT-Shell in den Vordergrund bringen AWT, Swing, JavaFX & SWT 2
vogella JOptionPane in den Vordergrund bringen AWT, Swing, JavaFX & SWT 2
S JOptionPane in den Bildschirmvordergrund bringen AWT, Swing, JavaFX & SWT 3
TRunKX JAva Fenster in den Vordergrund bringen AWT, Swing, JavaFX & SWT 7
G JFrame in den Vordergrund bringen AWT, Swing, JavaFX & SWT 8

Ähnliche Java Themen

Neue Themen


Oben