3

In java I have a custom class, and I sort it like this:

public static void sortList(List<FishCategory> categories) {
    Collections.sort(categories, new Comparator<FishCategory>(){
        public int compare(FishCategory s1, FishCategory s2) {
            return s1.getName().compareTo(s2.getName());
        }
    });
}

But like sql where you can do this:

select * from mytable
order by id, name

I want to double sort in java. I want to sort by this (Note: im using getParentId) as the first sort, then I want to sort like above.

public static void sortList(List<FishCategory> categories) {
    Collections.sort(categories, new Comparator<FishCategory>(){
        public int compare(FishCategory s1, FishCategory s2) {
            return s1.getParentId().compareTo(s2.getParentId());
        }
    });
}

I can't just run both functions one right after the next cause that would cancel out the first sorting. I need to sort the way sql does it (i.e. sort the sorted groups).

So I want to sort by .getParentId() first, then .getName().

Does anyone know a good way to do this easily?

Thanks

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
omega
  • 40,311
  • 81
  • 251
  • 474

4 Answers4

5

Write a single Comparator that tests both fields, consulting the second only if the first is equal. Plug that into the sort. Done.

Note that you can also write a Comparator that wraps around two Comparators (or a list of Comparators), if you want a more general solution for multi-level sorts.

keshlam
  • 7,931
  • 2
  • 19
  • 33
3

Simple modification for the Comparator:

final int diff = s1.getParentId() - s2.getParentId();
return diff != 0 ? diff : s1.getName().compareTo(s2.getName());

If getParentId() has a difference, sort it by the manner. Else sort by getName().

As pointed out by this comment by Pshemo, you can use the following in place for diff initialization:

= Integer.compare(s1.getParentId(), s2.getParentId());
Community
  • 1
  • 1
Unihedron
  • 10,902
  • 13
  • 62
  • 72
  • getParentId returns an integer, compare to wont take ints. – omega Jul 09 '14 at 01:42
  • @omega It auto-boxes to become an `Integer`. `java.lang.Integer` has `compareto()` specified. – Unihedron Jul 09 '14 at 01:43
  • I tried it. Eclipses complains and says compare to wont work for primitive type int. – omega Jul 09 '14 at 01:45
  • ok I changed the return type of getParentId to Integer, and then ur previous answer worked. – omega Jul 09 '14 at 01:50
  • @omega Ah but if it's using the primitive `int`, please keep it that way. Using `Integer` will force the interpreter to box an `int` whenever `getParentId()` is called. Most of the times using the operator `-` is preferable over `compareTo()`. – Unihedron Jul 09 '14 at 01:51
  • 1
    ok Im using your current answer and went back to primitive int. – omega Jul 09 '14 at 01:53
  • 2
    Don't use `s1.getParentId() - s2.getParentId()` unless you are entirely sure that you are subtracting only positive values. To be safe it is better to use `Integer.compare(x, y)` like in this case `diff = Integer.compare(s1.getParentId(),s2.getParentId());` – Pshemo Jul 09 '14 at 01:56
  • @Pshemo Actually the use of "Id" in the method implied that they are unique positive values. But thanks for the point - I'll add to my answer! – Unihedron Jul 09 '14 at 01:57
  • I can't use Integer.compare because it needs android api 19, but my minimum is 16. The getParentId only returns values >= 0, so I think the other way should be ok. – omega Jul 09 '14 at 02:03
3

Since Java 8 Comparator has thenComparing default method in which you can add next comparator which should be used if "original" comparator would decide that values are equal.

So your code can look like

public static void sortList(List<FishCategory> categories) {
    Collections.sort(categories, new Comparator<FishCategory>(){
        public int compare(FishCategory s1, FishCategory s2) {
            return s1.getParentId().compareTo(s2.getParentId());
        }
    }.thenComparing(new Comparator<FishCategory>(){
        public int compare(FishCategory s1, FishCategory s2) {
            return s1.getName().compareTo(s2.getName());
        }
    }));

    Comparator.comparing(FishCategory::getParentId);
}

or you can simplify it more with Comparator.comparing method which accept Function which returns Comparable field, so in your case you could even use lambdas and write something like

public static void sortList(List<FishCategory> categories) {
    Collections.sort(categories, Comparator
            .comparing(FishCategory::getParentId)
            .thenComparing(Comparator.comparing(FishCategory::getName)));
}
Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • 1
    Fancy Java 8 `Comparator` builders! Are there similar alternatives for Java 7? – Unihedron Jul 09 '14 at 01:52
  • I'm actually using this for android, and according to this http://stackoverflow.com/questions/23318109/is-it-possible-to-use-java-8-for-android-development it says java 8 is not supported, but still this looks like a good answer. – omega Jul 09 '14 at 01:58
2

First check it using the parentId, if it is equal then compare for the name

See the code below:

public static void sortList(List<FishCategory> categories) {
    Collections.sort(categories, new Comparator<FishCategory>(){
        public int compare(FishCategory s1, FishCategory s2) {
            int diff = s1.getParentId() - s2.getParentId();
            if (diff != 0)
                return diff;
            return s1.getName().compareTo(s2.getName());
        }
    });
}
Unihedron
  • 10,902
  • 13
  • 62
  • 72
shikjohari
  • 2,278
  • 11
  • 23