28

I am creating a simple program to learn about the Java Comparator class. I have sorted an Arraylist into order but now I want to sort the list in descending order but am having problems in where to call the .reverseOrder() method as I have used an inner class that implements Comparator<Song> (song being a song class which houses getters and setter methods).

Here is my SongSort class which houses the sorting process etc.;

import java.util.*;
import java.io.*;

public class SongSort
{
    ArrayList<Song> songList = new ArrayList<Song>();

    public void main(String[] args)
    {
        new SongSort().go();
    }

    class ArtistCompare implements Comparator<Song>
    {
        public int compare(Song one, Song two)
        {
            return one.getRating().compareTo(two.getRating());
        }
    }


    public void go()
    {

        getSongs();
        System.out.println(songList);
        //Collections.sort(songList); 
        System.out.println(songList);

        ArtistCompare artistCompare = new ArtistCompare();
        Collections.sort(songList, artistCompare);
        System.out.println(songList);
    }



    public void getSongs()
    {
        try{
            File file = new File("SongListMore.txt");
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line = null;

            while((line = reader.readLine()) != null)
               {
                   addSong(line);
               }
            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
        }

        public void addSong(String lineToParse)
        {
            String [] tokens = lineToParse.split("/");
            Song nextSong = new Song(tokens[0],  tokens[1], tokens[2], tokens[3]);
            songList.add(nextSong);

    }

}

And here is my simple Song class;

public class Song //implements Comparable<Song>
{
    private String title;
    private String artist;
    private String rating;
    private String bpm;

    public Song(String t, String a, String r, String b)
    {
        title = t;
        artist = a;
        rating = r;
        bpm = b;
    }

    public String getTitle()
    {
        return title;
    }

    public String getArtist()
    {
        return artist;
    }
    public String getRating()
    {
        return rating;
    }
    public String getBpm()
    {
        return bpm;
    }

    public String toString()
    {
       return ("Title : " + title + "," +  " Artist : " + artist +  " Rating : " + rating);
    }
}

Can anyone help me figure out where I will call the reverseOrder() method in the SongSort class, as it won't compile?

Null
  • 1,950
  • 9
  • 30
  • 33
James Morrison
  • 283
  • 1
  • 3
  • 4

4 Answers4

84
ArtistCompare artistCompare = new ArtistCompare();
Collections.sort(songList, Collections.reverseOrder(artistCompare));

Edit July 2015

As this answer still gets some attention, here a small update:

With Java SE 8 it's becoming easier to create a reversed comparator:

Comparator<Song> songRatingComparator = Comparator.comparing(Song::getRating);
Collections.sort(songList, songRatingComparator.reversed());

And you can, of course, also use the Streams framework:

List<Song> sortedSongList = songList.stream()
.sorted(Comparator.comparing(Song::getRating).reversed())
.collect(Collectors.toList());
Puce
  • 37,247
  • 13
  • 80
  • 152
  • What if I want to create comparator by 2 fields by double field ascending and by string field descending? – gstackoverflow Dec 13 '18 at 13:03
  • @gstackoverflow Please have a look at the Javadoc. There are similar methods for double properties (comparingDouble, thenComparingDouble) and there are overloaded methods where you can pass other Comparators such as reverseOrder()) – Puce Dec 14 '18 at 20:02
  • You can do `songList.sort(Comparator.comparing(Song::getRating).reversed())`, no need to stream it (unless original list is immutable) – wilmol Feb 27 '20 at 01:48
6

One way to implement an reverse order comparator is to implement an Compartor-Delegate that invert the comparator result (by changing the order).

public class ReverseOrder<T> implements Comparator<T> {
  private Comparator<T> delegate;
  public ReverseOrder(Comparator<T> delegate){
    this.delegate = delegate;
  }

  public int compare(T a, T b) {
    //reverse order of a and b!!!
    return this.delegate.compare(b,a);
  }
}

So the only thing you need to do is to use this delegate. For example:

  Comparator myComparator = new myComparator();
  List list = ...;
  List reverse = new ArrayList(list);

  //acceding
  Collections.sort(list, myComparator);

  //descending
  Collections.sort(list, new ReverseOrder(myComparator));
ekangas
  • 843
  • 1
  • 10
  • 17
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • 2
    java.utils.Collections.reverseOrder(Comparator c) will do the same and invert the comparison of your comparator. So you shouldn't reinvent the wheel here but use the Java SE API. – Peter Wippermann Jul 11 '13 at 12:00
  • 2
    Thats why Puce's answer is the accepted answer. -- Anyway I will not delete this answer because it is a valid one. – Ralph Jul 11 '13 at 14:08
  • I like this answer because it is more robust, and Comparators have more uses specifically than sorting existing lists. For example, I was able to make use of this in a TreeMap(Comparator) construction, which is a SortedMap. – ekangas Mar 06 '14 at 22:23
4

Let's take a simple example we have a class Person with two fields name age and we want to sort an existing collection of persons based on their age so let's assume that we have a class Person with a constructor and add the persons into the list and then sort them unsing the method sort of collection :

Person bachiri = new Person (17,"bachiri");
Person taoufiq = new Person (14,"Taoufiq");
Person abderrahman = new Person (15,"abderrahman");
List<Person> persons =  new ArrayList<>();

and this this the impelemtation of Agecomparable :

class AgeComparator implements Comparator<Person>{


    @Override
    public int compare(Person person1, Person person2) {
        return Integer.compare(person1.getAge(),person2.getAge());
    }


}

the trick is to multiple the return method with -1 so the final result will be reversed: class AgeComparator implements Comparator{

    @Override
    public int compare(Person person1, Person person2) {
        return -1 * Integer.compare(person1.getAge(),person2.getAge());
    }


}

so now we can get a reversed result :

Collection.sort (Persons, new AgeComparator());
  • 1
    I just saw @Ralph answer it's practical you can eighter switch the params a,b or write `return -1 *this.delegate.compare(a,b);` – Bachiri Taoufiq Abderrahman May 21 '17 at 11:23
  • **little tune** `@Override public int compare(Person person1, Person person2) { if (person1.getAge() == null || person2.getAge == null) { return 1; } return Integer.compare(person2.getAge(),person1.getAge()); } ` – Alisher Gulov Mar 16 '23 at 06:32
-8

If you need to use a Comparator which reverses the current order, just return a negative value in the compare method.

public class ComparatorInverse implements Comparator<Object> {
   @Override
   public int compare(Object lhs, Object rhs) {
      return -1;
   }
}
marnaish
  • 2,266
  • 1
  • 21
  • 17
  • That's not correct. Have a look at the Javadoc: http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html#compare%28T,%20T%29 – Puce May 29 '13 at 23:19
  • Returning a fixed value independent of the arguments does not make any sense at all, not to mention the reverse order case. Just swap the arguments and now they are ordered differently - that won't result in any stable order at all. – Alexander Klimetschek Jun 07 '13 at 16:46
  • I see I do not keep the contraints. Even if it makes no sense, it works for me. – marnaish Jun 07 '13 at 19:45
  • I guess in your code you first insert objects in a sorted order, in something like ArrayList which keeps the insertion order. Now calling sort on that list with the `ComparatorInverse` will call `compare()` in a fixed and predictable way with always `lhs < rhs`. Then returning -1 reverses that order. But if you insert your elements (say numbers) randomly or the sort algorithm changes, it will no longer produce the right result in terms of the natural sort oder of whatever `Object` is. – Alexander Klimetschek Jun 12 '13 at 21:37
  • Yes the items are in order - In my solution I wrote to reverse the current order. The Collections.sort() method will call a mergesort which switches all elements in the sorting (by returning -1). And of course you are right if they will change the sorting algorithm it may mess it up :) - so it's not correct. – marnaish Jun 15 '13 at 14:25