Blog-Archiv

Montag, 21. Februar 2022

Java Template Driven Comparator

Did you ever have the need to sort a list after a template list?
For instance, you always want to appear "Cut", "Copy", "Paste" menu items in that given order, but your resource reader delivers the item labels in an uncertain random order?

Comparator

Here is a generic Comparator that can sort items according to a template list. Values not found in the template list will be sorted to end.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.Comparator;
import java.util.List;

public abstract class TemplateDrivenComparator<T> implements Comparator<T>
{
    /**
     * @return the list of objects in template sort order.
     */
    protected abstract List<T> template();
    
    @Override
    public int compare(T o1, T o2) {
        // estimate template position for both elements
        int index1 = template().indexOf(o1);
        int index2 = template().indexOf(o2);
        
        // sort unknown to end
        if (index1 == -1)
            index1 = Integer.MAX_VALUE;
        
        if (index2 == -1)
            index2 = Integer.MAX_VALUE;
        
        // try to sort unknown values by optional override
        if (index1 == Integer.MAX_VALUE && index2 == Integer.MAX_VALUE)
            return notInTemplate(o1, o2);
        
        return index1 - index2;
    }
    
    /**
     * Called by <code>compare()</code> when neither of the compared values
     * was in template. To be overridden. This default implementation returns 0.
     * @param o1 first object that was not found in template.
     * @param o2 second object that was not found in template.
     * @return a <code>compare()</code> result for given values. 
     */
    protected int notInTemplate(T o1, T o2) {
        return 0;
    }
}

This is an abstract class that requires you to implement template(), see line 9. The template() implementation must return a list of objects that is in the desired sort order.

The compare() method on line 12 will be called by Collections.sort(list, comparator), see example below. On line 14 and 15 it fetches the indexes of given objects in the template list, which is done by calling equals() in indexOf(). Essentially that is all to be done when returning it as index1 - index2 on line 28. Every list passed to a comparator derived from TemplateDrivenComparator will then have exactly that sort order, given that all elements of the list to sort can be found in the template list.

In case a sort item passed to compare() is not in the template list, it will be sorted to end. This is done by lines 18 to 22. In case both sort items can not be found in the template list, the overridable method notInTemplate() on line 38 will be called. By default it returns zero, which means there is no sort order.

You could now override notInTemplate() to call another TemplateDrivenComparator, this creates a separate sort order at end of list. Or you could extend a base comparator and join its super.template() list to another list to be returned from template(), this allows to put either list to start or to end. Use Stream.concat().collect(Collectors.toList()) for that.

Example

Here is how to use this abstract class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.*;

public class Main
{
    public static void main(String[] args) {
        final List<String> sequence = new ArrayList<>();
        sequence.add("Copy");
        sequence.add("Paste");
        sequence.add("Cut");
        System.out.println(sequence);
        
        final Comparator<String> comparator = new TemplateDrivenComparator<String>()
        {
            private final List<String> sorted = List.of(
                    "Cut", 
                    "Copy", 
                    "Paste");
            @Override
            protected List<String> template() {
                return sorted;
            }
        };
        Collections.sort(sequence, comparator);
        System.out.println(sequence);
    }
}

Lines 6 to 9 allocate an unordered example list. It is printed out on line 10.

[Copy, Paste, Cut]

The desired order is given by the template list on lines 14 to 17, returned by the TemplateDrivenComparator.template() implementation on line 19.

On line 23 the list is sorted through given comparator, and then printed out on line 24.
Result is:

[Cut, Copy, Paste]

Resume

Hope this was helpful. I couldn't find any example of such a comparator on the web.




Keine Kommentare: