Collections.sort

Ich habe ein Problem mit dieser Methode. Un zwar hab ich eine Liste auf File-Objekten. Das sind audio-Dateien. Ich lese in einer Schleife diese Objekte ein, ermittel den Titel und den Interpreten und füge das dann der Liste hinzu : “Titel - Interpret”. So nun habe ich aber ein Problem.
Ich füge die Dateinamen der Lieder hinzu, wo kein Titel vorhanden is
Ich habe die Liste vorher mit nem button sortiert, das funktioniert auch weiterhin. Nur ist diesmal das Problem, das zwar die dateien alphabetisch sortiert werden, die ich nicht umbenannt habe, aber die die ich umbenannt habe, nicht. Also ganz oben sind meine umbenannten in willkürlicher Reihenfolge und untendrunter dann die nicht umbenannten alphabetisch Hier der ausschnitt wo das ganze passiert:

            Object[] al = getAnzeigeModel().toArray();
            Object[] f = getListModel().toArray();
            List l = Arrays.asList(al);
            List all = Arrays.asList(f);
            if (sort) {
                Collections.sort(l);
                Collections.sort(all);
            } else {
                Collections.shuffle(l);
                Collections.shuffle(all);
            }
            getListModel().clear();
            getAnzeigeModel().clear();
            for (Object o : f) {
                File op = (File) o;
                getListModel().addElement(op);
                Map properties = null;
                try {
                    properties = new MpegAudioFileReader().getAudioFileFormat(op).properties();
                } catch (UnsupportedAudioFileException | IOException ex) {
                    System.err.println(op + " ist keine gültige Audiodatei!");
                }
                if (properties != null) {
                    Object title = properties.get("title");
                    Object interpret = properties.get("author");
                    if (title == null || title.equals("") || interpret == null || interpret.equals("")) {
                        getAnzeigeModel().addElement(op.getName());
                    } else {
                        getAnzeigeModel().addElement(title + " - " + interpret);
                    }
                } else {
                    getAnzeigeModel().addElement(op.getName());
                }
                if (sort) {
                    Collections.sort(l);
                }
            }
            playList();```

Hab das sort nochmal ans ende gepackt, gleiches ergebnis.
Zur Info: "anzeigeModel" Ist das was angezeigt wird und "listmodel" das mit dem intern gearbeitet wird.
Mach ich irgendwas falsch?

Als Tipp: Verwende zusätzlich zum l auch noch I, i, í, ì und î als Dateinamen, und auf keinen Fall <Generics>, sonst kann ja jeder den Code gleich verstehen. Was schwer zu schreiben ist, soll auch schwer zu lesen sein :o)

Mal im Ernst: Das sieht ein bißchen konfus aus. Was es mit der Trennung des “Anzeige-Modells” und der “Internen Modells” auf sich hat, hat sich mir nicht ganz erschlossen.

Aber nochmal schrittweise: Du hast intern eine Liste mit “File”-Objekten. Und nun willst du diese File-Objekte in einer JList anzeigen. In dieser Liste soll entweder Titel+Interpret erscheinen, oder (wenn das nicht vorhanden ist), der Dateiname. BEIDE Listen (also sowohl die interne, als auch die angezeigte) sollen sortiert sein, und zwar nach dem String der angezeigt wird (und nicht nach dem Namen der Datei, aus der der angezeigte String generiert wurde). Stimmt das so weit?

ja stimmt so weit. Und Das das noch bissl konfus is stimmt, hab das auch eher mehr schnell als recht gemacht :slight_smile:

            Object[] intern = getListModel().toArray();
            List anzeigeList = Arrays.asList(getAnzeigeModel().toArray());
            List internList = Arrays.asList(getListModel().toArray());
            if (sort) {
                Collections.sort(anzeigeList);
                Collections.sort(internList);
            } else {
                Collections.shuffle(anzeigeList);
                Collections.shuffle(internList);
            }
            getListModel().clear();
            getAnzeigeModel().clear();
            for (Object o : intern) {
                File curFile = (File) o;
                getListModel().addElement(curFile);
                Map properties = null;
                try {
                    properties = new MpegAudioFileReader().getAudioFileFormat(curFile).properties();
                } catch (UnsupportedAudioFileException | IOException ex) {
                    System.err.println(curFile + " ist keine gültige Audiodatei!");
                }
                if (properties != null) {
                    Object title = properties.get("title");
                    Object interpret = properties.get("author");
                    if (title == null || title.equals("") || interpret == null || interpret.equals("")) {
                        getAnzeigeModel().addElement(curFile.getName());
                    } else {
                        getAnzeigeModel().addElement(title + " - " + interpret);
                    }
                } else {
                    getAnzeigeModel().addElement(curFile.getName());
                }
                if (sort) {
                    Collections.sort(anzeigeList);
                }
            }
            playList();
        }```

So nochmal mit anderen Variablen-Namen

Wenn ich das richtig sehe: Du sortierst zwar die Listen, aber arbeitest nur mit dem Array intern.

OK… hab’ mir das nochmal angesehen, aber … irgendwie wurde es nicht klarer. Warum liegen die “Files” in einem ListModel, und nicht in einer normalen List? Ansonsten scheinen da auch einge Sachen vermischt zu sein: Das Erstellen der Strings (für die jeweiligen Files) hat ja nichts mit der Sortierung zu tun. Und dass sortieren und shufflen nicht (in dieser) Form gemischt sein sollte, ist IMHO auch klar. Es könnte angebracht sein, eine eigene Klasse zu erstellen, wie “Song”, die eine File und einen “Anzeigestring” zusammenfasst, aber das ist schwer zu sagen.

Wie auch immer, hier mal, was ich gerade geschrieben hatte, aber ob das so sinnvoll ist, weiß ich gerade nicht.

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.swing.DefaultListModel;
import javax.swing.ListModel;

public class ListSorting
{
    public static void main(String[] args)
    {
        ListSorting listSorting = new ListSorting();
        
        System.out.println("Before sorting:");
        listSorting.printInfo();
        
        listSorting.sort(true);
        
        System.out.println("After sorting:");
        listSorting.printInfo();
    }

    private void sort(boolean sort)
    {
        if (getListModel().isEmpty()) 
        {
            return;
        }
        List<File> files = getAsList(getListModel());
        List<String> strings = new ArrayList<String>();
        for (File file : files)
        {
            strings.add(stringFor(file));
        }
        List<Integer> order = null;
        if (sort)
        {
            order = computeSortingOrder(
                strings, ListSorting.<String>comparableComparator());
        }
        else
        {
            order = computeRandomOrder(files.size());
        }
        
        strings = permute(strings, order);
        files = permute(files, order);
        
        getListModel().clear();
        getAnzeigeModel().clear();
        for (int i=0; i<files.size(); i++)
        {
            File file = files.get(i);
            getListModel().addElement(file);

            String string = strings.get(i);
            getAnzeigeModel().addElement(string);
        }
        playList();
    }
    
    private static <T> List<Integer> computeSortingOrder(
        List<T> list, final Comparator<? super T> comparator)
    {
        class IndexedElement
        {
            int index;
            T element;
        }
        List<IndexedElement> indexedElements = new ArrayList<IndexedElement>();
        int index = 0;
        for (T element : list)
        {
            IndexedElement indexedElement = new IndexedElement();
            indexedElement.element = element;
            indexedElement.index = index++;
            indexedElements.add(indexedElement);
        }
        Collections.sort(indexedElements, new Comparator<IndexedElement>()
        {
            @Override
            public int compare(IndexedElement ie0, IndexedElement ie1)
            {
                return comparator.compare(ie0.element, ie1.element);
            }
        });
        List<Integer> result = new ArrayList<Integer>(
            Collections.nCopies(list.size(), 0));
        for (int i=0; i<list.size(); i++)
        {
            IndexedElement indexedElement = indexedElements.get(i);
            result.set(indexedElement.index, i);
        }
        return result;
    }
    
    private static List<Integer> computeRandomOrder(int n)
    {
        List<Integer> result = new ArrayList<Integer>();
        for (int i=0; i<n; i++)
        {
            result.add(i);
        }
        Collections.shuffle(result);
        return result;
    }
    
    private static <T> List<T> permute(List<T> list, List<Integer> permutation)
    {
        List<T> result = new ArrayList<T>(
            Collections.nCopies(list.size(), (T)null));
        for (int i=0; i<list.size(); i++)
        {
            T element = list.get(i);
            int newIndex = permutation.get(i);
            result.set(newIndex, element);
        }
        return result;
    }
    
    private static <V extends Comparable<? super V>> 
        Comparator<V> comparableComparator()
    {
        return new Comparator<V>()
        {
            @Override
            public int compare(V v0, V v1)
            {
                return v0.compareTo(v1);
            }
        };
    }
    
    private String stringFor(File file)
    {
        Map<String, Object> properties = getProperties(file);
        if (properties != null)
        {
            Object title = properties.get("title");
            Object interpret = properties.get("author");
            if (title != null && !title.equals("") && 
                interpret != null && !interpret.equals(""))
            {
                return title + " - " + interpret;
            }
        }
        return file.getName();
    }
    
    private static <T> List<T> getAsList(ListModel<T> listModel)
    {
        List<T> result = new ArrayList<T>();
        for (int i=0; i<listModel.getSize(); i++)
        {
            result.add(listModel.getElementAt(i));
        }
        return result;
    }
    
    //=== Dummy stuff =========================================================
    
    void printInfo()
    {
        List<String> strings = getAsList(getAnzeigeModel());
        List<File> files = getAsList(getListModel());
        System.out.printf("%30s %30s
", "File", "String");
        for (int i=0; i<files.size(); i++)
        {
            File file = files.get(i);
            String string = strings.get(i);
            System.out.printf("%30s %30s
", file, string);
        }
    }
    
    private DefaultListModel<File> listModel = new DefaultListModel<File>();
    private DefaultListModel<String> anzeigeModel = new DefaultListModel<String>();
    
    ListSorting()
    {
        listModel.addElement(new File("C:/fileB.txt"));
        listModel.addElement(new File("C:/fileC.txt"));
        listModel.addElement(new File("C:/fileA.txt"));
        anzeigeModel.addElement("(none)");
        anzeigeModel.addElement("(none)");
        anzeigeModel.addElement("(none)");
    }
    
    private Map<String, Object> getProperties(File file)
    {
        if (file.toString().contains("fileB"))
        {
            Map<String, Object> result = new LinkedHashMap<String, Object>();
            result.put("title", "fileB_Title");
            result.put("author", "fileB_Author");
            return result;
        }
        return null;
    }
    
    private void playList()
    {
    }

    private DefaultListModel<File> getListModel()
    {
        return listModel;
    }
    private DefaultListModel<String> getAnzeigeModel()
    {
        return anzeigeModel;
    }
}

@_Michael Also die listen werden schon geshuffled bzw sortiert, nur nicht so wie es sein soll.
@Marco13 : Ich könnte die auch in einer ganz normalen Liste zusammenfassen, klar, aber Ich habs lieber gleich in ein listModel gepackt, damit ich zur not der GUI noch eine Liste hinzufügen kann, die die interne Liste anzeigt, was ich auch schon einmal getan habe. Aber okay, ich werde mir deinen Code mal genauer ansehen

Es ging nicht zuletzt darum, dass du deine Intentionen hinter bestimmten Dingen ggf. etwas ausführlicher beschreiben solltest. Als “reine Datenhaltung” ist ein ListModel vergleichsweise unhandlich (das sieht man z.B. schon daran, dass man es nicht direct shufflen und sorten kann). In der Anzeige braucht man aber ein ListModel. Die Fragen, wann (und für welche Aktionen) auf die eine oder die andere Liste zugegriffen wird, könnten relevant sein. Da, wenn ich das richtig sehe, in beiden Listen eigentlich das gleiche (und immer in der gleichen Reihenfolge) enthalten ist, könnte man da vielleicht einiges vereinfachen. Wie schon angedeutet: Ich denke, eine Klasse wie

class Song
{
    private File file; // oder URL
    private String name;
    ...
}

könnte nicht schaden. Dort könnte man ggf. auch noch weitere Metainformationen drin unterbringen, die man aus dieser “Properties”-Map liest. So könnte man z.B. eine Liste mit Song-Objekten erstellen. In der JList könnte man die dann anzeigen (entweder direkt, wenn Song#toString den Titel zurückgibt, oder mit einem Renderer). Und wenn man einen Song in der JList auswählt, könnte man in einem Panel nebendran automatisch alle properties anzeigen, die man aus dieser Map gelesen hat. Man könnte auch einfach verschiedene Comparatoren erstellen, so dass man die Lieder z.B. nach Titel, Länge, Name des Interpreten usw. sortieren könnte. Nur ein Gedanke…

In deinen Listen sind File Objekte? Dann mach daraus eine List schau dir mal an was Genericssind. Dann stellt sich die Frage, nach was sollen die File Objekte sortiert werden? Nach dem Pfad? Dann musst du da afaik einen geeigneten Comparator schreiben, der eben den absolutePath der File Objekte vergleicht. Von alleine geht das nicht.

@bERt0r Ja, standardmäßig sind Files “Comparable”, und es wird nach dem Pfadnamen verglichen. In diesem Fall ist es aber etwas spezieller: Es soll wohl nach dem String verglichen werden, der FÜR die File angezeigt wird. Das ist der in meinem Snippet dann mit der “stringFor(File)”-Methode erstellt wird. Eigentlich könnte man das direkt machen, und einen Comparator verwenden, der eben nicht die Files vergleicht, sondern die Strings, die mit “stringFor” erstellt werden. Allerdings werden ja u.U. VIELE Vergleiche gemacht, und das “stringFor” scheint relativ aufwändig zu sein (da werden irgendwelche Dateien analysiert und properties gelesen). Deswegen wäre es vermutlich keine gute Idee, das direkt im Comparator bei jedem Vergleich zu machen. Diese Strings nur EINmal zu erstellen wäre nahe liegend. Aber wenn die Files und die Strings in getrennten Listen liegen, wird das Sortieren hakelig: Die beiden Listen müssen immer gleich sortiert sein und so. Das ganze dann noch im Zusammenspiel mit den ListModels ist ziemlich frickelig. Deswegen glaube ich, dass so eine eigene “Song”-Klasse viele Vorteile bieten würde (und in Anbetracht dessen, worum es in dem Programm zu gehen scheint, ohnehin angebracht wäre ;-))

Ich habe diese Song-Klasse mal erstellt, hast ja recht die gehört da irgendwie noch rein. Ich werde mir mal an den Stellen,an der die Dateien eingelesen werden, jeweils so eine Liste machen und die dann zusammenfügen und dann sortieren. Ich schau mal ob ich das hinkrieg und wen nicht, lest ihr wieder von mir :slight_smile:

Habs hingekriegt, wens interessiert, die Sortieren-Methode schaut nun so aus:

            Object[] f = getListModel().toArray();
            List l = Arrays.asList(f);
            Collections.sort(l);
            getListModel().clear();
            getAnzeigeModel().clear();
            for (Object o : f) {
                Song song = (Song) o;
                getListModel().addElement(song);
                String title = song.getTitle();
                String interpret = song.getInterpret();
                if (title == null || title.isEmpty() || interpret == null || interpret.isEmpty()) {
                    getAnzeigeModel().addElement(song.getAudioFile().getName());
                } else {
                    getAnzeigeModel().addElement(title + " - " + interpret);
                }
            }
            playList();
        }```
Und die Song-Klasse, der ich nen Comparator verpassen musste und nun funktioniert das Ding auch:
```package MusikPlayer;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import javax.sound.sampled.UnsupportedAudioFileException;
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;

public class Song implements Comparable<Song> {

    private Map properties;
    private String title;
    private String interpret;
    private String album;
    private String date;
    private String id3Tag;
    private int channels;
    private int mPegVersion;
    private int framesize;
    private int mp3VersionLayer;
    private int frameLength;
    private int bitrate;
    private String encoding;
    private ByteArrayInputStream id3TagV2;
    private String id3TagV2Version;
    private boolean padding;
    private long byteLength;
    private double framerate;
    private long duration;
    private int frequency;
    private int headerPos;
    private String comment;
    private boolean original;
    private File song;

    public Song(File song) {
        this.song = song;
        try {
            properties = new MpegAudioFileReader().getAudioFileFormat(song).properties();
        } catch (UnsupportedAudioFileException | IOException ex) {
            System.err.println(song + " ist keine gültige Audio-Datei!");

        }
        if (properties != null) {
            this.title = (String) properties.get("title");
            this.interpret = (String) properties.get("author");
            this.album = (String) properties.get("mp3.id3tag.orchestra");
            this.date = (String) properties.get("date");
            this.id3Tag = (String) properties.get("mp3.id3tag.track");
            this.channels = (int) properties.get("mp3.channels");
            this.mPegVersion = Integer.parseInt((String) properties.get("mp3.version.mpeg"));
            this.framesize = (int) properties.get("mp3.framesize.bytes");
            this.mp3VersionLayer = Integer.parseInt((String) properties.get("mp3.version.layer"));
            this.frameLength = (int) properties.get("mp3.length.frames");
            this.bitrate = (int) properties.get("mp3.bitrate.nominal.bps");
            this.encoding = (String) properties.get("mp3.version.encoding");
            this.id3TagV2 = (ByteArrayInputStream) properties.get("mp3.id3tag.v2");
            this.id3TagV2Version = (String) properties.get("mp3.id3tag.v2.version");
            this.padding = (boolean) properties.get("mp3.padding");
            this.byteLength = (int) properties.get("mp3.length.bytes");
            this.framerate = Double.parseDouble(Float.toString((float) properties.get("mp3.framerate.fps")));
            this.duration = (long) properties.get("duration");
            this.frequency = (int) properties.get("mp3.frequency.hz");
            this.headerPos = (int) properties.get("mp3.header.pos");
            this.comment = (String) properties.get("comment");
            this.original = (boolean) properties.get("mp3.original");


        }
    }

    public Map getProperties() {
        return properties;
    }

    public String getTitle() {
        return title;
    }

    public String getInterpret() {
        return interpret;
    }

    public String getAlbum() {
        return album;
    }

    public String getDate() {
        return date;
    }

    public File getAudioFile() {
        return song;
    }

    public String getId3Tag() {
        return id3Tag;
    }

    public int getChannels() {
        return channels;
    }

    public int getmPegVersion() {
        return mPegVersion;
    }

    public int getFramesize() {
        return framesize;
    }

    public int getMp3VersionLayer() {
        return mp3VersionLayer;
    }

    public int getFrameLength() {
        return frameLength;
    }

    public int getBitrate() {
        return bitrate;
    }

    public String getEncoding() {
        return encoding;
    }

    public ByteArrayInputStream getId3TagV2() {
        return id3TagV2;
    }

    public String getId3TagV2Version() {
        return id3TagV2Version;
    }

    public boolean isPadding() {
        return padding;
    }

    public long getByteLength() {
        return byteLength;
    }

    public double getFramerate() {
        return framerate;
    }

    public long getDuration() {
        return duration;
    }

    public int getFrequency() {
        return frequency;
    }

    public int getHeaderPos() {
        return headerPos;
    }

    public String getComment() {
        return comment;
    }

    public boolean isOriginal() {
        return original;
    }

    @Override
    public String toString() {
        return properties.toString();
    }

    @Override
    public int compareTo(Song o) {
        String thisTitle;
        String otherTitle;

        if (getTitle() == null || getTitle().isEmpty()) {
            thisTitle = getAudioFile().getName();
        } else {
            thisTitle = getTitle();
        }

        if (o.getTitle() == null || o.getTitle().isEmpty()) {
            otherTitle = o.getAudioFile().getName();
        } else {
            otherTitle = o.getTitle();
        }
        
        return thisTitle.compareTo(otherTitle);
    }
}```
  • Immer noch keine Generics
  • ist sichergestellt, dass die Properties immer alle Daten enthalten?
  • wieso getProperties, sollte die Song-Klasse das nicht kapseln?
  • Denglisch (MusikPlayer? interpret?)
  • Comparator ist nicht das gleiche wie Comparable (just sayin’)
            Object[] f = getListModel().toArray();
            List l = Arrays.asList(f);
            Collections.sort(l);
            getListModel().clear();
            for (Object o : f) {
                getListModel().addElement(song);

sieht nach ganz schön umständlichen Weg aus,
aber getListModel() scheint zumindest keine Liste zu sein, nun gut,

dennoch könntest du dich vielleicht auf das Array beschränken,
was macht Collections.sort() mit deiner extra erzeugten Liste?

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
	Object[] a = list.toArray(); 
	Arrays.sort(a);
        // + noch Liste zurückbefüllen

pffff, und das verwendet noch nichtmal f sondern eine zweites Array

wollte jetzt noch schreiben for (Object o : f) { geht auch mit dem Array f,
aber das ist ja schon das Array f… :wink:


    public int compareTo(Song o) {
        String thisTitle;
        String otherTitle;
 
        if (getTitle() == null || getTitle().isEmpty()) {
            thisTitle = getAudioFile().getName();
        } else {
            thisTitle = getTitle();
        }
 
        if (o.getTitle() == null || o.getTitle().isEmpty()) {
            otherTitle = o.getAudioFile().getName();
        } else {
            otherTitle = o.getTitle();
        }
       
        return thisTitle.compareTo(otherTitle);
    }```
(möglichst) keinen Code wiederholen, und seien es auch nur 4-5 Zeilen,
schreibe eine Methode getTitleForCompare() und dann in compareTo nur 
`return getTitleForCompare().compareTo(o.getTitleForCompare());`
das spart dann auch gleich noch lokale Variablen

@Landei :

  • Die generics hab ich an der einen Stelle weggelassen, weil ich List iwie hässlich finde, werds dann aber doch hinschreiben, zur Leserlichkeit
  • Das ist sichergestellt, da JLayer bei nicht vorhanden sachen einfach null reinschreibt
  • stimmt, ich entferne das mal, danke
  • Ja, ich schreib aus Reflex eig sehr oft deutsch da rein und dadurch kommst das so raus, versuche es aber eigentlich komplett deutsch zu halten :o
  • Hab da eig nie nen UNterschied egsehen, aber bei genauerer betrachtung hast du recht :x
    @SlaterB :

Habs dahingehend geändert, wusste nur den umweg über collections :o

und zu dem wiederholtem Code: Hab ich auch weggemacht und ne extra Methode erstellt.

Danke fürs Feedback :slight_smile:

Ich push das Thema einfach nochmal, da ich mit der gleichen Methode nun ein anderes Problem hab.
Codezeilen sind folgende:

        if (!getListModel().isEmpty()) {
            Song[] songs = new Song[listModel.getSize()];
            listModel.copyInto(songs);
            switch (option) {
                case 0:
                    break;
                case 1:
                    Arrays.sort(songs, new TitleComparator());
                    break;
                case 2:
                    Arrays.sort(songs, new InterpretComparator());
                    break;
                case 3:
                    Arrays.sort(songs, new ShortDurationComparator());
                    break;
                case 4:
                    Arrays.sort(songs, new LongDurationComparator());
                    break;
            }
            getListModel().clear();
            getAnzeigeModel().clear();
            for (Song song : songs) {
                getListModel().addElement(song);
                String title = song.getTitle();
                String interpret = song.getInterpret();
                if (title == null || title.isEmpty() || interpret == null || interpret.isEmpty()) {
                    getAnzeigeModel().addElement(song.getAudioFile().getName());
                } else {
                    getAnzeigeModel().addElement(title + " - " + interpret);
                }
            }
            playList();
        }
    }```
Die Methode Wir hierdurch aufgerufen:
```sort(sortOptions.getSelectedIndex());``` Das ist eine Combobox.

So und wenn ich nun nach Interpreten sortieren will, fliegt diese Exception:

Exception in thread “AWT-EventQueue-0” java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:747)
at java.util.TimSort.mergeAt(TimSort.java:483)
at java.util.TimSort.mergeCollapse(TimSort.java:410)
at java.util.TimSort.sort(TimSort.java:214)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at MusikPlayer.MusikPlayer.sort(MusikPlayer.java:669)
at MusikPlayer.MusikPlayer.sortOptionsActionPerformed(MusikPlayer.java:699)
at MusikPlayer.MusikPlayer.access$1300(MusikPlayer.java:41)
at MusikPlayer.MusikPlayer$12.actionPerformed(MusikPlayer.java:247)
at javax.swing.JComboBox.fireActionEvent(JComboBox.java:1260)
at javax.swing.JComboBox.setSelectedItem(JComboBox.java:588)
at javax.swing.JComboBox.setSelectedIndex(JComboBox.java:624)
at javax.swing.plaf.basic.BasicComboPopup$Handler.mouseReleased(BasicComboPopup.java:835)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:290)
at java.awt.Component.processMouseEvent(Component.java:6505)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
at javax.swing.plaf.basic.BasicComboPopup$1.processMouseEvent(BasicComboPopup.java:499)
at java.awt.Component.processEvent(Component.java:6270)
at java.awt.Container.processEvent(Container.java:2229)
at java.awt.Component.dispatchEventImpl(Component.java:4861)
at java.awt.Container.dispatchEventImpl(Container.java:2287)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
at java.awt.Container.dispatchEventImpl(Container.java:2273)
at java.awt.Window.dispatchEventImpl(Window.java:2719)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue$4.run(EventQueue.java:708)
at java.awt.EventQueue$4.run(EventQueue.java:706)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)


Nach bissl googlen hab ich rausgefunden das es an dem Comparator liegt, der schaut so aus:
```import MusikPlayer.Song;
import java.util.Comparator;

public class InterpretComparator implements Comparator<Song> {

    @Override
    public int compare(Song song1, Song song2) {
        if (song1.getInterpret() != null && song2.getInterpret() != null) {
            return song1.getInterpret().compareTo(song2.getInterpret());
        } else {
            return 1;
        }
    }
}```

Das sollte dafür sorgen, das alle Lieder nach Interpret sortiert werden und die Lieder die keinen haben einfach nach unten wandern. Also oben alle nach interpret sortiert und unten dann der rest. Aber wie soll ich das sonst lösen?

Du darfst im Fall das beide Songs keinen Interpreten haben nicht 1 zurückgeben. Ansonsten kommt folgendes Konstrukt dabei rum:
Seien A und B Songs ohne Interpreten. Einmal wird A mit B verglichen, ein zweites mal B mit A. Dein Comparator macht daraus: A < B und B < A. Ich würde da 0 zurückgeben, das bedeutet dann dass Songs ohne Interpret nicht sortierbar sind.

Ich würde das außerdem noch etwas weiter verfeinern. Was passiert wenn nur song1.getInterpret() null ist, was passiert wenn nur song2.getInterpret() null ist?

Wenn ich das mache werden die ohne Interpret aber einfach mitten reingeschmissen, wenn man nur die interpreten beachtet stimmt die Reihenfolge allerdings. Also irgendwie auch nich so ganz das was ich will

Edit: Ich muss grad feststellen, wenn ich nach name sortiere, sind ganz oben die mit Titel und darunter die, die keinen haben, so wie es hier eig sein soll o.O

nach interpret schauts momentan so aus mit dem return 0:

Beide Comparatoren nochmal:


    @Override
    public int compare(Song song1, Song song2) {
        if (song1.getInterpret() != null && song2.getInterpret() != null) {
            return song1.getInterpret().compareTo(song2.getInterpret());
        } else {
            return 0;
        }
    }
}```
```public class TitleComparator implements Comparator<Song> { 
       
    @Override
    public int compare(Song song1, Song song2) {
        return song1.getTitleForCompare().compareTo(song2.getTitleForCompare());
    }

} ```
``` public class Song implements Comparable<Song> {
//...
  public String getTitleForCompare() {
        if (getTitle() == null || getTitle().isEmpty()) {
            return getAudioFile().getName();
        } else {
            return getTitle();
        }
    }
//...
}```

gesagt wurde was absolut notwendig ist, wie ein sinkendes Boot abzudichten,
über Kurskorrekturen kann man danach nachdenken, angesprochen wurde es auch schon,

wie gesagt überlegen was in welchen Fällen passieren muss, bei 2x null 1 zurückzugeben hilft gar nicht,
interessant sind die Fälle mit 1x null, 1x nicht null, in beiden möglichen Varianten,

da kann eine komplizierte 4fach-Unterscheidung bauen,
oder trickreich auch wieder mit einer Methode ähnlich getTitleForCompare() arbeiten,

hier getInterpretForCompare(), entweder den Interpret geben oder “ZZZZZZZZZ” oder ähnliches für null,
dann sorgenfrei diesen Wert aus beiden vergleichen, null landet als “ZZZZZZZZZ” ziemlich weit hinten,
2x “ZZZZZZZZZ” ergibt richtigerweise auch 0 im compare von String

evtl noch was anderes hinten anfügen, damit die Reihenfolge aller nuller und aller sonstigen mit gleichen Interpreten nicht zufällig ist,
ein Zweitsortierkriterium,

Nebenbei bemerkt ist sort(int option) auch nicht das Gelbe vom Ei. Eine “stabile” Liste von Optionen sollte durch ein Enum abgebildet werden. Und dann braucht man auch kein hässliches switch mehr, denn Enums können ja Interfaces implementieren. Mal aus’m Kopp in die Feder diktiert:

public enum SongComparator implements Comparator<Song> {
  BY_TITLE {
     public int compare(Song first, Song second) { ... }
  },
  BY_INTERPRETE {
     public int compare(Song first, Song second) { ... }
  },
  BY_SHORT_DURATION {
     public int compare(Song first, Song second) { ... }
  },
  BY_LONG_DURATION {
     public int compare(Song first, Song second) { ... }
  }
}

Jetzt hast du deine Optionen (BY_TITLE, BY_SHORT_DURTATIOn u.s.w.), die gleich wissen, wie dafür sortiert werden muss.

Generell ist es eine gute Daumenregel, wenn man versucht ist, ein switch zu schreiben: “Überlege, ob du es wirklich brauchst. Und falls ja, überlege nochmal, nur etwas angestrengter.”

Das mit dem “ZZZZZZZZZ” würde ich als Hack bezeichnen. Schon sowas wie “getSomethingForCompare” ist etwas fragwürdig. Ich finde, den Fall zu behandeln, dass ein Lied eine bestimmte (für den Vergleich benötigte) Sache nicht hat, sollte Aufgabe des Comparators oder der Compare-Methode sein. Ausgehend von der ursprünglich kritisierten Wiederholung in

        @Override
        public int compareTo(Song o) {
            String thisTitle;
            String otherTitle;
     
            if (getTitle() == null || getTitle().isEmpty()) {
                thisTitle = getAudioFile().getName();
            } else {
                thisTitle = getTitle();
            }
     
            if (o.getTitle() == null || o.getTitle().isEmpty()) {
                otherTitle = o.getAudioFile().getName();
            } else {
                otherTitle = o.getTitle();
            }
           
            return thisTitle.compareTo(otherTitle);
        }

(ich finde das nicht schlimm, aber) da könnte man sich notfalls eine Methode machen wie

        private static getTitleForCompare(Song s) {
            if (s.getTitle() == null || s.getTitle().isEmpty()) {
                return s.getAudioFile().getName();
            }
            return s.getTitle();
        }

(static!) die man im Comparator einmal mit “this” und einmal mit “other” aufruft.

Beim Fall des Interpreten könnte man - je nachdem, wie Titel ohne Interpret eingeordnet werden sollen (zuerst oder zuletzt?) - etwas ähnliches machen. Wenn man’s drauf anlegt, könnte man da ziemlich allgemein dumrumengineeren, aber sowas wie

    public class InterpretComparator implements Comparator<Song> {
        @Override
        public int compare(Song song1, Song song2) {
            
            if (song1.getInterpret() == null) {
                if (song2.getInterpret() == null) {
                    return 0;
                }
                return -1;
            }
            if (song2.getInterpret() == null) {
                return 1;
            }
            return song1.getInterpret().compareTo(song2.getInterpret());
        }
    }

sollte es schon tun. (Mit “rumengineeren” meinte ich, dass man sich eine Methode machen könnte, die (grob, aus dem Kopf) so aussieht


private static <T extends Comparable<? super T>> compareNullFirst(T t0, T t1)
{
    if (t0 == null)
    {
        if (t1 == null)
        {
            return 0;
        }
        return -1;
    }
    if (t1 == null)
    {
        return 1;
    }
    return t0.compareTo(t1);
}

Und analog dazu eine “compareNullLast”. Die könnte man im Comparator dann mit

        public int compare(Song song1, Song song2) {
            return compareNullFirst(song1.getInterpret(), song2.getInterpret());
        }

aufrufen. Und zwar in jedem Comparator, der zwei Comparables vergleicht, die potentiell “null” sind!