Okay maybe not lightning fast, but relatively it is a huge improvement to what it was before : ).
I'll be doing a code review later to see if there are other improvements I can do. It already does things like checking if any values are unselected in a column before qualifying. I eliminated all "scanning" behaviors (that I know of) and instead built event-driven HashMaps.
Okay this is working awesome but since I'm pushing this to the highest quality possible, I want to resolve a problem with concurrent cell value updates (especially since I want this to play nice with RxJavaFX). I may need some thoughts here and yes, all updates are happening on the JavaFX thread :)
I attached a ConcurrentTableFilterTest application class which will schedule values in one column to be updated on a fixed ExecutorService. If you filter any columns while these values are updating, it will likely throw an IllegalStateException that I have deliberately declared in the DupeCounter<T> class.
The core logic is supported by ColumnFilter<T,R> and DupeCounter<T>, the latter which keeps track of duplicate counts for each distinct value. If a count for a given value becomes 1, that value is added to a collection of distinct values. If the count drops to 0, it will remove that item from the distinct values. Going below 0 will logically throw an IllegalStateException as this indicates it tried to remove something that is no longer existent, which means something went wrong with the tracking.
Two instances of DupeCounter are used, one to support the management of distinct filter values, and the other to keep track of distinct "visible values" (which drive the grayed out values when they fall out of scope).
For some reason, the "visible" distinct values are struggling with these cells updating concurrently because it tries to drop the DupeCounter below to 0, which throws an IllegalStateException. I was tempted to throw out this check but I really want to understand why it is doing it first. I have confirmed there is no race condition and everything is happening on the JavaFX thread. Does anybody want to help code review this one behavior? This code is not as beastly as it used to be.
Okay I figured it out. The IllegalStateException should not be thrown when it comes to driving the grayed out values as they fall out of scope. This check is irrelevant and illogical because if a cell value is updated by a background process, but that cell is hidden from view due to an existing filter, the cell's visibility is already irrelevant. But trying to remove the hidden cell's old value from the DupeCounter will cause the count to go below 0.
So I just disabled the DupeCounter's IllegalStateException for the grayed-out values feature. It is still in place for the distinct filter value management. Classifying this as a problem would be pretty perfectionist and not worth the effort for now.
The impact of not addressing is this: having an existing filter applied, if another column's values are changing the grayed out mechanism may not be accurate for that column. Like I said, it's a perfectionist pursuit.
So I'm done for real this time. I encourage checking out the ConcurrentTableFilterTest to see a demonstration of the dynamic updating. Please merge whenever you are ready. Let me know if you have any questions or comments. Overall, I'm much happier with this control and there should be a real difference in performance and quality, especially with automatic accounting for cell values changing.
Of course, the sample UI's are all in there, including the ConcurrentTableFilterTest.
There is one more complication I found and I might be creating a memory leak. When I call TableColumn#getCellObservableValue I assumed it returned a stored, reused instance rather than a new one. I don't think this is the behavior, so the way I track the listeners with a HashMap<CellIdentity<R>,ChangeListener<R>> is completely useless, where CellIdentity<R> is this: