blue-sky
blue-sky

Reputation: 53836

Custom sort of BasicDBObject not sorting as expected

In below code I'm attempting to custom sort by date descending a list of BasicDBObject.

The output printed is :

{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:59"}
{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:20"}
{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:00"}
{ "year" : "2015" , "month" : "01" , "day" : "23" , "time" : "07:30:59"}
{ "year" : "2015" , "month" : "09" , "day" : "26" , "time" : "07:30:59"}

when it should be :

{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:59"}
{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:20"}
{ "year" : "2015" , "month" : "12" , "day" : "1" , "time" : "07:30:00"}
{ "year" : "2015" , "month" : "09" , "day" : "26" , "time" : "07:30:59"}
{ "year" : "2015" , "month" : "01" , "day" : "23" , "time" : "07:30:59"}

as September (09) occurs after January (01).

Is there an issue with sort compare method?

code :

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;


public class SortByDate
{

    private void testSort()
    {

        List<DBObject> l = new ArrayList<DBObject>();

        DBObject dbo = new BasicDBObject();
        dbo.put("year", "2015");
        dbo.put("month", "12");
        dbo.put("day", "1");
        dbo.put("time", "07:30:20");
        l.add(dbo);

        dbo = new BasicDBObject();
        dbo.put("year", "2015");
        dbo.put("month", "09");
        dbo.put("day", "26");
        dbo.put("time", "07:30:59");
        l.add(dbo);

        dbo = new BasicDBObject();
        dbo.put("year", "2015");
        dbo.put("month", "12");
        dbo.put("day", "1");
        dbo.put("time", "07:30:59");
        l.add(dbo);

        dbo = new BasicDBObject();
        dbo.put("year", "2015");
        dbo.put("month", "01");
        dbo.put("day", "23");
        dbo.put("time", "07:30:59");
        l.add(dbo);

        dbo = new BasicDBObject();
        dbo.put("year", "2015");
        dbo.put("month", "12");
        dbo.put("day", "1");
        dbo.put("time", "07:30:00");

        l.add(dbo);

        DateTimeFormatter dtf = org.joda.time.format.DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss");

        Collections.sort(l, new Comparator<DBObject>()
        {
            @Override
            public int compare(final DBObject o1, final DBObject o2)
            {
                long m1 = dtf.parseMillis((String) o1.get("month")+"/"+(String) o1.get("day")+"/"+(String) o1.get("year")+" "+(String) o1.get("time"));
                long m2 = dtf.parseMillis((String) o2.get("month")+"/"+(String) o2.get("day")+"/"+(String) o2.get("year")+" "+(String) o2.get("time"));

                return (int)m2 - (int)m1;
            }
        });

        for(DBObject d : l){ 
            System.out.println(d.toString()); 
        }

    }

    public static void main(String args[])
    {
        new SortByDate().testSort();
    }

}

Upvotes: 0

Views: 265

Answers (1)

AlexW
AlexW

Reputation: 1166

The problem is here:

return (int)m2 - (int)m1;

You convert long millis to int, but value is larger than Integer.MAX_VALUE, so overflow happens and conversion returns wrong result (it can be negative, for example). Correct conversion could be:

return (int)(m2 - m1);

But again, if date differs a lot, overflow still can happen. Correct and safe way to do what you need is to use Long.compare:

return Long.compare(m2, m1);

Upvotes: 1

Related Questions