2

I am writing my own comparator class called PercentComparator and called the sort as follows

Collections.sort(engineList, new PercentageComparator());

where engineList is List of objects, where each object has percent complete values, and above sort functionality is working fine.

Now customer is asking to add another order by element by its product type along with percent. Can we do sort order by two elements of objects?

Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
skalluri
  • 53
  • 1
  • 8

9 Answers9

4
Collections.sort(engineList, new PercentageComparator());
Collections.sort(engineList, new ProductTypeComparator());

Sorts by product type and for equal product types sorts further by percentage. This works because

This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.

http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#sort%28java.util.List%29

Ishtar
  • 11,542
  • 1
  • 25
  • 31
3

Create your own new Comparator that calls the PercentageComparator after comparing the product type.

JustinKSU
  • 4,875
  • 2
  • 29
  • 51
  • Can we assume that the Java sort methods are stable sorting algorithms? – David R Tribble Jan 31 '12 at 20:44
  • 1
    Collections.sort is stable. Quote from API: "For example, the algorithm used by sort does not have to be a mergesort, but it does have to be **stable**." – msi Jan 31 '12 at 20:47
  • Using Guava's Ordering: `productOrdering.compound(Ordering.from(percentageComparator))` combines the orderings in turn. That might be slightly more convenient. (http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html) – Louis Wasserman Jan 31 '12 at 21:57
  • @LouisWasserman I think Guava is awesome for stuff like this if you are allowed to use it. I would post that as a possible answer. You will get my upvote. Cheers. – JustinKSU Feb 01 '12 at 00:20
  • May I edit your answer to provide an example of exactly how to do this in Guava? I'm not sure of the social protocol, but I'd be happy to write up examples with code. – Louis Wasserman Feb 01 '12 at 00:24
1

If I got it right:

class EngineComparator implements Comparator<Engine> {
    @Override
    public int compare(Engine o1, Engine o2) {
        int result = o1.getProdType().compareTo(o2.getProdType());
        return (result == 0) ? o1.getPercent().compareTo(o2.getPercent()) : result;
    }
}

Following is how it sorts the collection:


Prod-Type   Percent
=======================
  A         1
  A         2
  A         3
  B         1
  B         2
  B         3
  C         1
  C         2
  C         3
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
  • thank you , that works, the same kind of example is also given in http://stackoverflow.com/questions/4258700/collections-sort-with-multiple-fields – skalluri Feb 01 '12 at 17:05
0

If it's going to be seperate sorting do a seperate comparator and use them in 'chain'. Or do your EngineComparator(Boolean sortPercentDesc, Boolean sortProductDesc), which in my opinion could be better, as would be easier to maintain.

msi
  • 2,609
  • 18
  • 20
0

Your sort method can do any kind of ordering you want it to, just as long as all of the pertinent (comparable) information is contained in the objects passed to the method. In other words, your sortable objects should contain all of the sortable subfields. And you'll have to wriet a diferrent sort method/class to deal with it.

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
0

You could create a compound Comparator that has two Comparators and returns the value from the second Comparator if the first Comparator returns 0.

You could even expand this process using a list of N Comparators and return the first non-zero result or returns 0 if the end of the list is reached.

Dev
  • 11,919
  • 3
  • 40
  • 53
0

Typically your Comparator would test one field at a time until there is a difference. For example, if percent took top priority, followed by product type, and your class had the clever name StackOverflow1:

    Comparator<StackOverflow1> COMPARATOR = new Comparator<StackOverflow1>() {

          @Override
          public int compare(StackOverflow1 o1, StackOverflow1 o2) {
             int result = Double.compare(o1.percent, o2.percent);
             if (result == 0)
                result = o1.productType - o2.productType;
                // NOTE - above line isn't really safe but used for illustration...

             // any more tests of fields here...

             return result;
          }         
   };

If you want a ton of flexibility, by all means write a bunch of individual Comparators and chain them together (as others have suggested), but many times that it overkill - you only need the one.

user949300
  • 15,364
  • 7
  • 35
  • 66
  • thanks for input, that is a good solution, but for product type is high priority than percent complete, your post is useful. – skalluri Feb 01 '12 at 17:06
0

First create another comparator implementation which compares only on product type. Then call this:

Collections.sort(engineList, new CompoundComparator(productTypeCmp, percentageCmp));

Here is the implementation of a Compound comparator, it delegates the comparison to the comparators passed in, in the order they were passed in.

class CompoundComparator implements Comparator<Engine>{ 
    private List<Comparator> comparators;
    public CompoundComparator(Comparator<Engine> ... comparators){
       this.comparators = Arrays.asList(comparators);
    }
    public int compare(Engine o1, Engine o2){
       int cmp = 0;
       Iterator cmpIter = comparators.iterator();
       while(cmp == 0 && cmpIter.hasNext()){
          cmp = cmpIter.next().compare(o1, o2);
       }
       return cmp;
    }

}

Assuming the objects are of type Engine.

driangle
  • 11,601
  • 5
  • 47
  • 54
0

For a more general solution, take a look at Apache Common's ComparatorChain, from the Javadocs:

A ComparatorChain is a Comparator that wraps one or more Comparators in sequence. The ComparatorChain calls each Comparator in sequence until either 1) any single Comparator returns a non-zero result (and that result is then returned), or 2) the ComparatorChain is exhausted (and zero is returned). This type of sorting is very similar to multi-column sorting in SQL, and this class allows Java classes to emulate that kind of behaviour when sorting a List.

Óscar López
  • 232,561
  • 37
  • 312
  • 386