2014-09-06 4 views
6

Das JavaDoc für den ListChangeListener stellt eine Vorlage für das Behandeln von Änderungen bereit. Ich weiß jedoch nicht, wie ich mit Permutationen umgehen soll. Für jeden Index kann ich herausfinden, wo der neue Index des Artikels ist, aber ich weiß nicht, was ich damit machen soll. Dies ist ein kleines Puzzle, das unabhängig von der Programmiersprache ist. Eine ObservableList kann nur add(), remove(), set(), und hat auch einen Iterator.ListChangeListener wasPermutated-Block

Wenn ich eine ursprüngliche Liste [1,2,3] habe, und eine Liste [] an sie binden, muss die gebundene Liste [1,2,3] übereinstimmen. Wenn die ursprüngliche Liste den Komparator vertauscht, so dass die ursprüngliche Liste nun [3,2,1] lautet, wie mache ich die gebundene Liste mit?

/** 
* Binds a source list's elements to a destination list. Any changes made in 
* the source list will reflect in the destination list. 
* 
* @param <SRC> The source list's object type. 
* @param <DEST> The destination list's object type. 
* @param dest The destination list that will be bound to the src list. 
* @param src The source list to watch for changes, and propagate up to the 
* destination list. 
* @param transformer A function that will transform a source list data 
* type, A, into a destination list data type, B. 
*/ 
public static <SRC, DEST> void bindLists(
     ObservableList<DEST> dest, ObservableList<SRC> src, Function<? super SRC, ? extends DEST> transformer) { 
    /*Add the initial data into the destination list.*/ 
    for (SRC a : src) { 
     dest.add(transformer.apply(a)); 
    } 
    /*Watch for future data to add to the destination list. Also watch for removal 
    of data form the source list to remove its respective item in the destination 
    list.*/ 
    src.addListener((ListChangeListener.Change<? extends SRC> c) -> { 
     while (c.next()) { 
      if (c.wasPermutated()) { 
       /*How do you handle permutations? Do you remove and then add, 
       or add and then remove, or use set, or use a copy arraylist 
       and set the right indices? Removing/adding causes concurrent modifications.*/ 
       for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); oldIndex++) { 
        int newIndex = c.getPermutation(oldIndex); 
        dest.remove(oldIndex); 
        dest.add(newIndex, dest.get(oldIndex)); 
       } 
      } else if (c.wasUpdated()) { 

      } else { 
       /*Respond to removed data.*/ 
       for (SRC item : c.getRemoved()) { 
        int from = c.getFrom(); 
        dest.remove(from); 
       } 
       /*Respond to added data.*/ 
       for (SRC item : c.getAddedSubList()) { 
        int indexAdded = src.indexOf(item); 
        dest.add(indexAdded, transformer.apply(item)); 
       } 
      } 
     } 
    }); 
} 
+0

Ich würde auch gerne wissen, wie wasUpdated() selbst ausgelöst wird. Müssen die Objekte der ObservableList Observable oder etwas Ähnliches implementieren? –

Antwort

6

Für die Permutation Fall würde ich nicht die Mühe, es versucht add() und remove() zu verwenden zu tun zu behandeln. Dies wird dazu führen, dass sich die Indizes verschieben und Dinge verwirrend machen (zumindest für mich).

Konzeptionell erhalten Sie eine Reihe von betroffenen Elementen und ein Array mit einigen Zahlen, die angeben, wo jedes Element verschoben wurde. Ich denke du verstehst das sehr. In Ihrem Code, den Sie haben,

newIndex = getPermutation(oldIndex); 

was bedeutet, dass das Element bei oldIndex Bedürfnisse war bewegt newIndex werden. Die Falte ist, dass Sie, wenn Sie die Bewegung direkt ausführen, ein Element überschreiben, das noch nicht verschoben wurde. Ich denke, der einfachste Weg, damit umzugehen, besteht darin, eine Kopie des betroffenen Teilbereichs zu erstellen und dann einfach durch das Permutations-Array zu gehen und Elemente von der Kopie in ihre neuen Positionen zu verschieben. Der Code, dies zu tun ist:

int from = c.getFrom(); 
    int to = c.getTo(); 
    List<DEST> copy = new ArrayList<>(dest.subList(from, to)); 
    for (int oldIndex = from; oldIndex < to; oldIndex++) { 
     int newIndex = c.getPermutation(oldIndex); 
     dest.set(newIndex, copy.get(oldIndex - from)); 
    } 

Dies ist eine Permutation ist, so dass jedes Element irgendwo endet, und keiner hinzugefügt oder gelöscht werden. Dies bedeutet, dass Sie den Listenbereich nicht kopieren müssen und dass Sie Elemente einzeln nach der Bewegungskette verschieben können, während Sie nur ein einzelnes Element mit temporärem Speicherplatz verwenden. Es könnte mehrere Zyklen von Ketten geben, also müsstest du das auch erkennen und handhaben. Das klingt ziemlich komplex. Ich überlasse das einem anderen Beantworter. :-) Für mein Geld ist das Kopieren des betroffenen Bereichs einfach und leicht verständlich.

Die Permutations- und aktualisierten Änderungsmodi werden nicht durch normale Listenaktionen ausgelöst. Wenn Sie sich javafx.collections.ObservableListBase ansehen, können Sie ein Protokoll sehen, das eine Listenimplementierung verwenden kann, um Informationen zu einer bestimmten Änderung zu erstellen. Wenn die Implementierung die richtigen Informationen an die Methoden nextPermutation oder nextUpdate liefert, werden diese weiteren Änderungsmodi ausgelöst. Ich bin mir nicht sicher, was sie in JavaFX auslösen könnte. Zum Beispiel könnten die Methoden Node.toFront() und Node.toBack() zur Änderung der Reihenfolge der Knotenstapel möglicherweise Permutationsänderungen generieren, scheinen dies aber nicht zu tun. Ich weiß auch nichts, das eine Update-Änderung erzeugen würde.

Semantisch, ich denke eine Aktualisierung Änderung bedeutet, dass die Elemente in diesem Bereich der Liste geändert haben, aber dass die Länge der Liste gleich bleibt. Dies steht im Gegensatz zum Änderungsmodus "Ersetzt", bei dem eine Reihe von Elementen durch eine andere Anzahl von Elementen ersetzt werden kann. Es könnte auch sein, dass die Update-Änderung bedeutet, dass die Elemente selbst nicht ersetzt wurden, dh der Inhalt der Liste hat sich nicht geändert, lediglich dass sich die internen Zustände der Elemente geändert haben.

+2

Im Allgemeinen würde ich diesen Ansatz bevorzugen. Beachten Sie, dass, wenn die Liste, die Sie bearbeiten, eine Liste von untergeordneten Knoten in einer "Fläche" ist (oder sein könnte), dies bricht, da während der Verarbeitung der Schleife doppelte Knoten in der Liste vorhanden sind. In diesem Fall können Sie die Permutation mit copy.set (newIndex - from, dest.get (oldIndex));) innerhalb der Schleife auf die Kopie anwenden. Wenn Sie fertig sind, ersetzen Sie die Elemente von 'dest' durch' dest_List (von, to) .clear(); 'und' dest.addAll (from, copy); '. –

+1

@James_D Guter Punkt zum Vermeiden doppelter 'Knoten'-Einträge, wenn' dest 'im Szenendiagramm ist. –

2

Beachten Sie, dass das, was Sie hier tun, in EasyBind implementiert ist. Sehen Sie, wie zu map und bind eine Liste.

Im Wesentlichen ist dies, wie Sie Ihre bindLists Methode implementieren:

public static <SRC, DEST> void bindLists(
     ObservableList<DEST> dest, ObservableList<SRC> src, 
     Function<? super SRC, ? extends DEST> transformer) { 

    EasyBind.listBind(dest, EasyBind.map(src, transformer)); 
} 
+0

Ich erinnere mich, dass ich vor ein paar Monaten entschieden habe, deine Klasse aus irgendeinem Grund nicht zu benutzen, aber ich kann mich nicht erinnern, was. Ich nehme an, dass ich an dieser Stelle wieder zu der Verwendung wechseln kann. :) –

+0

Auch außerhalb des Themas Frage, aber was ist der Unterschied zwischen einem Javadoc-Glas und einem Quellglas? Ist die EasyBind.jar nicht schon die Quelle? Warum muss ich eine Quelle anhängen? –

+0

Um EasyBind zu verwenden, brauchen Sie nur 'easybind- .jar'. Es enthält nur kompilierte Klassen. Das Quell-Jar enthält nur den Quellcode. Es ist nützlich, das Quelljar in Ihrer IDE anzuhängen, damit es Ihnen die Javadoc-Kommentare und/oder den Quellcode anzeigen kann, zum Beispiel wenn Sie den Code debuggen und schrittweise bearbeiten. –