Tip: How to generically sort unknown objects by their nested properties

The input data I want to sort has the following format:

[A,10][B,9][C,8][D,7][E, 6][F, 5][G, 4][H, 3][I, 2][J, 1]

I want to be able to sort this input on both pr. letter and number no matter what the object type is or the object structure is. The following JUnit-test demonstrates how to sort:


public class GenericSortUtilTest extends TestCase
{
    public class AVersion {
        private int version;
        public AVersion(int v) { this.version = v; }
        public int getVersion() { return version; }
    }
    public class A {
        private String display;
        private AVersion aVersion;
        public A(String s, AVersion av) { this.display = s; this.aVersion = av;}
        public String getDisplay() { return display + "v" + aVersion.getVersion(); }
        // refer to this using its exact name, but without 'get'
        public AVersion getAVersion() { return aVersion; }
    }
    public class B {
        private A aaa;
        public B(A a) { this.aaa = a; }
        public A getAaa() { return aaa; }
        public String toString() {return aaa.getDisplay();}
    }

    @Test
    public void testGenericArraySort()
    {
        B[] aList = new B[10];
        ArrayList<B> list = new ArrayList<B>();
        int version = 10;
        for ( int i = 0; i < 10; i++ )
        {
            char c = (char) ( 65 + i );
            aList[i] = new B( new A(c+"", new AVersion(version--)) );
            list.add(aList[i]);
        }

        Collections.shuffle(list);
        System.out.println("Before generic sort: " + list);
        aList = (B[]) list.toArray(new B[]{});

        try
        {
            String sortKeyDisplay = "aaa.display";
            String sortKeyVersion = "aaa.AVersion.version";
            B[] bs = GenericSortUtil.genericArraySort(aList, sortKeyVersion);
            list.clear();
            for(B b : bs) {
                list.add(b);
            }
            System.out.println("After generic sort: " + list);
        }
        catch ( ClassCastException e )
        {
            e.printStackTrace();
        }
        catch ( IllegalAccessException e )
        {
            e.printStackTrace();
        }
        catch ( InvocationTargetException e )
        {
            e.printStackTrace();
        }
        catch ( NoSuchMethodException e )
        {
            e.printStackTrace();
        }
    }
}

The initial input and it’s output after the generic sort is done:


Before generic sort: [Cv8, Dv7, Fv5, Ev6, Av10, Bv9, Iv2, Hv3, Gv4, Jv1]
After generic sort: [Jv1, Iv2, Hv3, Gv4, Fv5, Ev6, Dv7, Cv8, Bv9, Av10]

I can very easily change how the objects are sorted by just changing the sorting key. If I want to sort using the display names as key, the generic sort result will be:


Before generic sort: [Iv2, Ev6, Jv1, Cv8, Dv7, Gv4, Av10, Fv5, Hv3, Bv9]
After generic sort: [Av10, Bv9, Cv8, Dv7, Ev6, Fv5, Gv4, Hv3, Iv2, Jv1]

As you can see, I dont need to change the internal object structure to achieve the sorting. The only thing I need to know is the sorting key. In a web applikation you could even make your own tag library containing the GenericSortUtil class and achieve generic sorting directly from your JSP’s. The standard JSTL forEach tag has no generic sorting capability (not as I know about at least), so migrating code from elderly frameworks (like ATG Dynamo) that actually has build-in generic sorting will then be solved using the described approach in this blog post. If you need the tab-library code as well then please comment and I’ll publish it as well.

This entry was posted in Java and tagged . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>