FilteredList/SortedList
Martin Sladecek
martin.sladecek at oracle.com
Thu Jan 3 01:10:37 PST 2013
Hi Jonathan,
thanks for providing some use-cases for TableView, I will try to put
down some code examples below.
On 01/02/2013 07:56 PM, Jonathan Giles wrote:
> Hi Martin,
>
> These lists have always been driven by the requirements of TableView,
> but I'm having a hard time understanding how the newly proposed API
> will fit in (and what will be required of me and / or end users of the
> TableView control). It would be great if you could show some examples
> of how a developer would instantiate a TableView that is:
>
> 1) always in a sorted state
OK, this one should be simple.
tableView.setItems(myList.transform().sorted( (o1,o2) ->
doSomeComparison(o1, o2) );
Now this is sorting set explicitly by the developer, I understand you
might want to do some sorting based on columns. Then you can store
"items" internally (for the original order, they won't change) and use
some different field for the actual work, doing something like
setCurrentItems(items.transform().sorted(currentColumnComparator));
To return back to the original order, this should be enough:
setCurrentItems(items);
To modify the table (like adding a new item), you would use items
directly. The actual position where to put the new item in "items" can
be determined by calling getOriginalSourceIndex(int index) on the
previous table item from "currentItems".
> 2) able to filtered based on a user typing
Now this is something that was also problematic in the original API.
When the user types, the Predicate/Matcher changes, so originally we had
to keep the filteredlist reference and call
filteredList.setMatcher(matcher). As filtered list cannot rely on the
fact that the set matched by the new matcher is a subset of the previous
one, everything is processed again. Trying to create a meaningful
ListChangeListener.Change might result is a huge number of sub-changes,
so originally, I just fired a "everything was removed & new items were
added" change, which means whole table was rebuilt.
The same problems are in the new API -
* need to keep a reference to change the filter (in this case it's the
reference of the underlying list and a filtered() call on it when the
predicate changes)
* optimize cases where new predicate matches a superset/subset of the
previous predicate's set.
What about having another method for "mutable" predicates, like this?
filtered(ObservableValue<? extends Predicate<? super E>> predicate);
The filtered list would bind to the ObservableValue (which could be a
property, expression, etc...) and refilter on change. As the user types,
you just need to update the property with the new Predicate.
I didn't have a solution for the second problem in the original API, but
with JDK8 I can imagine something like Comparator function (but for
partially ordered set, which is what predicates form).
<T extends Predicate<? super E>> void filtered(ObservableValue<T>
predicate, BiFunction<T, T, Double> comparator)
where double works like in the standard Comparator, except it returns
NaN when the 2 predicates are incomparable.
> 3) is always sorted and able to be filtered based on user input
Same as #2, except called on a sorted list.
>
> I'm also particularly interested in knowing how sorting would change.
> Presently the code simply runs a comparator over the ObservableList,
> modifying the list in place. This is ok, but not ideal as it loses the
> original sort order which means it is not possible to go back to an
> unsorted state. I'm going to presume I would want to add special-case
> support in TableView so that, where possible, I can return the user
> back to an unsorted state by grabbing the original source from the
> transformation list. The alternative is for me to maintain a
> SortedList internally so that the original ObservableList is not
> modified, but then that is troublesome as it changes the behavior of
> the API. Any thoughts here would be much appreciated.
>
> Thanks,
> -- Jonathan
>
> On 3/01/2013 4:58 a.m., Martin Sladecek wrote:
>> Hello,
>>
>> I would like to start discussion about a new API for ObservableLists
>> that are filtered/sorted. This API was already in the repository
>> before 2.0 was released, but was withdrawn (
>> http://javafx-jira.kenai.com/browse/RT-15302).
>> In the original API, there were 3 new classes: FilteredList,
>> SortedList and TransformationList (parent of the previous two). These
>> 2 lists took a different (Observable)List, acting as a view for that
>> list, firing change notifications when the view changed.
>>
>> Since we have a different situation now, due to new (overlapping)
>> features in JDK 8, I'd like to propose a different API for
>> FilteredList and SortedList.
>>
>> First of all, I don't think we need separate FilteredList and
>> SortedList classes anymore. With defender methods, we can have a
>> similar approach to JDK's stream(), having e.g. transform() method on
>> ObservableList, that would allow filtering & sorting of the list.
>> The original API allowed "batch" mode, but this is now basically
>> covered with List's stream(), although it's more cumbersome as you
>> won't get a List directly of a Stream, not to mention ObservableList,
>> which is what you need to pass as a model to various controls, like
>> TableView.
>> Still, I'm not in favour of having this also in FX, but maybe
>> something like Iterable to ObservableList conversion method would
>> ease the FX development when using JDK 8 steams...
>>
>> I want to keep TransformationList to serve both as a common parent
>> for all current and future "transformation" lists, but also returning
>> TransformationList on transform() call, having methods like
>> filtered() and sorted() directly on the TransformationList.
>>
>> So overall, the new API would look like this:
>>
>> public abstract class TransformationList<E, F> implements
>> ObservableList<E> {
>>
>> protected TransformationList(ObservableList<? extends F> source);
>>
>> public final ObservableList<? extends F> getDirectSource();
>>
>> // The first non-transformation list in the chain.
>> public final ObservableList<?> getOriginalSource();
>>
>> // Called when a change from the source is triggered.
>> protected abstract void onSourceChanged(Change<? extends F> c);
>>
>> // Maps the index of this list's element to an index in the
>> direct source list.
>> public abstract int getSourceIndex(int index);
>>
>> public final int getOriginalSourceIndex(int index);
>>
>> public final TransformationList<E, E> filtered(Predicate<? super
>> E> predicate);
>>
>> public final TransformationList<E, E> sorted(Comparator<? super
>> E> comparator);
>>
>> }
>>
>> ObservableList<E> interface would get a new defender method:
>>
>> public TransformationList<E, E> transform();
>>
>> this would return TransformationList representation of the current list.
>>
>> Another appoach would be to have filtered(), sorted() directly on
>> ObservableList as a defender methods, which would remove the
>> necessity of calling transform() before filtered()/sorted(), but I
>> find the first one more consistent with JDK API.
>>
>> Any comments?
>>
>> Thanks,
>> -Martin
>>
>
-Martin
More information about the openjfx-dev
mailing list