How to sort a set of objects based on a certain field?

How to sort below set of objects, so that the returned filesDb set will have records sorted by fileDate field in this object?

In SampleFile object, it has these fields:
fileDate and fileId
file date is like:
2011-05-11
2011-05-10

private Set<SampleFile> getFiles(Set<SampleFile> files) {
    Set<SampleFile> filesDb = new HashSet();

    for (SampleFile f : files) {
      filesDb.add(fileDao.get(f.getFileId()));
    }
    return filesDb;
  }

Thanks.
heyday2004Asked:
Who is Participating?
 
for_yanCommented:
OK. this is the ultimate experiment with many files
witn the same dates and different dates.
Everything seesm tio work fine (see ouput below):

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

public class SortedFiles {

    public static void main(String [] args){


        SimpleDateFormat ff = new SimpleDateFormat("MM-dd-yyyy");


        HashSet set = new HashSet();

        SampleFile f1 = new SampleFile("a", new java.util.Date());

         SampleFile f2 = new SampleFile("b", new java.util.Date());

        SampleFile f3 = new SampleFile("c", ff.parse("03-01-2011", new ParsePosition(0)));

        SampleFile f4 = new SampleFile("d", ff.parse("03-15-2011", new ParsePosition(0)));

         SampleFile f5 = new SampleFile("e", ff.parse("03-15-2011", new ParsePosition(0)));

         SampleFile f6 = new SampleFile("f", ff.parse("04-11-2011", new ParsePosition(0)));




        set.add(f1);
        set.add(f2);
        set.add(f3);
        set.add(f4);
        set.add(f5);
         set.add(f6);

        System.out.println(set.size());
        ArrayList ar = new ArrayList();

        Iterator it1 =set.iterator();
        while(it1.hasNext()){
            SampleFile sf = (SampleFile)it1.next();
            ar.add(sf);
        }

        Collections.sort(ar,  new Comparator<SampleFile>() {
	    public int compare(SampleFile s1, SampleFile s2) {
		return s1.getFileDate().compareTo(s2.getFileDate());
	    }}) ;

        for(int j=0; j<ar.size(); j++){
            SampleFile sf = (SampleFile)ar.get(j);
            System.out.println(sf.getFileName() + " " + sf.getFileDate().toString());
        }



}

}

class SampleFile {
String fileName;
java.util.Date fileDate;

    SampleFile(String name, java.util.Date d){
        fileDate = d;
        fileName = name;
    }

public java.util.Date getFileDate(){
    return fileDate;
}

public String getFileName(){
    return fileName;
}


}

Open in new window


Output:

6
c Tue Mar 01 00:00:00 PST 2011
d Tue Mar 15 00:00:00 PDT 2011
e Tue Mar 15 00:00:00 PDT 2011
f Mon Apr 11 00:00:00 PDT 2011
b Sun May 22 22:06:10 PDT 2011
a Sun May 22 22:06:10 PDT 2011

Open in new window

0
 
for_yanCommented:
In the definition of the SampleFile class
you define the method
int compareTo(SampleFile f){

}

so that it should return poistibve when the date is later and negative oppsotely

After you've done it pt your objcets in a list
 Collections.sort(list)
method will produce ciorrectly ordered list

0
 
CEHJCommented:
Use a Comparator (assuming String)
Collections.sort(samples, new Comparator<SampleFile>() {
    public int compare(SampleFile s1, SampleFile s2) {
        return s1.getFileDate().compareTo(s2.getFileDate());
    }
});

Open in new window

0
Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

 
for_yanCommented:
You don't have to assume that date is  a string

you just return

if(s1.getFileDate().befor(s2.getFileDate())return -1;
else return 1;

It is not good idea to assume the dtaes are strings
0
 
CEHJCommented:
>>You don't have to assume that date is  a string

You have to use whatever data type it is - obviously ;)
0
 
for_yanCommented:
I'd rather say you have to use wahtever business requirements are -
if they ask you to order your onbjects by date - no matter
how thsi adte uis stored in your internal structire they would not
appreciate when your ordering will be wriong judging by date,
so if I'm programming this part I have to take care of this and
first parse strings into the dates and compare dates, if this is the requirement
0
 
CEHJCommented:
heyday2004 - to avoid any more pointless speculation - is the field Date or String?
0
 
CEHJCommented:
Actually - you can ignore that question sorry - my code example will work in either case
0
 
objectsCommented:
heres an example of how to do the sorting

http://helpdesk.objects.com.au/java/how-to-sort-java-list-using-custom-ordering

you'll also need to copy the sorted list into something like a LinkedHashSet to retain the order
http://download.oracle.com/javase/6/docs/api/java/util/LinkedHashSet.html
0
 
heyday2004Author Commented:
The field Date. Thanks!
0
 
for_yanCommented:
Yes, of course you have to compare it as dates, but CEHJ was right - he was lucky - I bet
he was thinknig of them as strings when he was writing this code, but
java.util.Date() overrides method compareTo() , so indeed his code works as you would probably need
0
 
heyday2004Author Commented:
Thanks for all the replies. what I need is to sort Set, not List.

Listed below is what I got from the replies, will it return a sorted Set (filesDb) by FileDate (that's all my purpose)? Any further suggestion to get a sorted set of filesDb (by FileDate)?

private Set<SampleFile> getFiles(Set<SampleFile> files) {
    Set<SampleFile> filesDb = new HashSet();

    for (SampleFile f : files) {
      filesDb.add(fileDao.get(f.getFileId()));
    }

    Collections.sort(filesDb, new Comparator<SampleFile>() {
    public int compare(SampleFile s1, SampleFile s2) {
        return s1.getFileDate().compareTo(s2.getFileDate());
    }
});

    return filesDb;

  }

Thanks a lot.
0
 
CEHJCommented:
Sorry, yes. You can either:

a. add the contents of the Set to a List and sort that
b. use the Comparator to maintain a SortedSet
0
 
CEHJCommented:
e.g.
private SortedSet<SampleFile> getSortedFiles(Set<SampleFile> files) {
	SortedSet<SampleFile> filesDb = new TreeSet(new Comparator<SampleFile>() {
	    public int compare(SampleFile s1, SampleFile s2) {
		return s1.getFileDate().compareTo(s2.getFileDate());
	    }
	});
	filesDb.addAll(files);
	return filesDb;
    }

Open in new window

0
 
CEHJCommented:
A small adjustment - should have been
SortedSet<SampleFile> filesDb = new TreeSet<SampleFile>(new Comparator<SampleFile>() {

Open in new window

0
 
objectsCommented:
> Thanks for all the replies. what I need is to sort Set, not List.

yes mentioned that earlier, you'll need to use a Set that supports ordering. nit a HashSet
0
 
heyday2004Author Commented:
Sorry for my late replies because I was out of town. One last question:
For CEHJ's solution:
If my original set has:
record A
record b

and if both records have exactly the same FileDate

Then seems in the final sortedSet, only record A will be added in the sortedSet (since it provides the same compare result).

Please advise if this is true when we used SortedSet (based on the running result, it seems to be true). Thanks!
0
 
for_yanCommented:
No, in CEHJ solution they will both be in the resulting TreeSet- you only cannot predict in which order
these two records will appear - which is waht should be expected because they have the
same date.
It is true that Set does not allow duplicates, but this is the Set of your objects as a whole,
so your objects will not be identical therefore they can both be memebers of the TreeSet,
it is only comparrator will retun zero, so their order relative to each other will not be defined,
 but none of these objects will be exuded from  the resulting ordered set.
So you should not worry about losing them.
0
 
for_yanCommented:

In general, if your collection of objects can contain identical objects
then you probably want to use List instead of Set.
The ordering part will work the same way, but List
can contain duplicate elements, whereas Set cannot.
But in your setting of the problem you used Set, so it is natural to assume
taht you don't have identiacl objects.
0
 
for_yanCommented:
You know, it looks I was avtually wriong - I'm doing some experiemnts
and it returns to me just one file when I start with two files
with the same date.
I'll continue, and let you know when I figure out.
It ios probably better not to use TreeSet
0
 
heyday2004Author Commented:
Thanks for the replies. I'm still confused since in CEHJ's solution, the input Set: files have object A and B. And my output of filesDb (sorted Set) seems only contain one object (A or B). The filesDb is a new sorted Set, with record added like this:

for (SampleFile f : files) {
  filesDb.addAll(file....);
}      
return filesDb;

Any possibility that in this case, the returned filesDb might just contain record A or B but not both?

Thanks!
0
 
for_yanCommented:
Yes, that's what I saw also.
I made a set of two files (objects having date and name)
and they had the same date and different name.
After sorting with that method of my original set
I got a SortedSet with only one object.
So I think we need to change it from TreeSet

0
 
for_yanCommented:

Ok. This is how it works with two files having thwe same date:

import java.util.*;

public class SortedFiles {

    public static void main(String [] args){

        HashSet set = new HashSet();

        SampleFile f1 = new SampleFile("a", new java.util.Date());

         SampleFile f2 = new SampleFile("b", new java.util.Date());

        set.add(f1);
        set.add(f2);

        System.out.println(set.size());
        ArrayList ar = new ArrayList();

        Iterator it1 =set.iterator();
        while(it1.hasNext()){
            SampleFile sf = (SampleFile)it1.next();
            ar.add(sf);
        }

        Collections.sort(ar,  new Comparator<SampleFile>() {
	    public int compare(SampleFile s1, SampleFile s2) {
		return s1.getFileDate().compareTo(s2.getFileDate());
	    }}) ;

        for(int j=0; j<ar.size(); j++){
            SampleFile sf = (SampleFile)ar.get(j);
            System.out.println(sf.getFileName());
        }



}

}

class SampleFile {
String fileName;
java.util.Date fileDate;

    SampleFile(String name, java.util.Date d){
        fileDate = d;
        fileName = name;
    }

public java.util.Date getFileDate(){
    return fileDate;
}

public String getFileName(){
    return fileName;
}


}

Open in new window


Output:
2
a
b

Open in new window

0
 
for_yanCommented:
You were right with being cautious about TreeSet.
I was thinking that it should use this Comapartor which we defined
only for establishing order. In fact it looks like it was using it for
add method aslo, and add method for TreeSet adds
element only if it is different from all other elements.
So when I was adding two files with different names but with the same date
it was not adding the second one as it was violating
that TereeSet should have all unique elements.
As a result I got the set with only one element.
Even when I overrode equals() explicitly in the Sample File class
that it should be equal only when all fields are equal, not just date, it still
was not adding the second object with the same date.
So probably with ArrayList, which does not have such issues,
it is much safer in such cases.
0
 
heyday2004Author Commented:
Many thanks. This works. So for sorted set, if the compare method returns 0, then even the hash code is different, the two records will be considered duplicate? Thanks.
0
 
for_yanCommented:
Probably. They have some words in the API, but these words sound a little bit ambiguous
So when I wrote my first message I went there and when I read those words, then I decided
to experuiment  and then realized that my initial understanding was not correct.

These are the words in
http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html

Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.


0
 
CEHJCommented:
>>Please advise if this is true when we used SortedSet (based on the running result, it seems to be true). Thanks!

Yes, it is true. Something odd going on which i can't explain at the moment:

http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html#add(E)

Looking at the docs above, i was surprised that the equals method wasn't even called on my test SampleFile object
0
 
CEHJCommented:
I now understand the problem: the TreeSet example can't (at least easily - i'd like to be able to force it, but can't see a way at the moment) be used because the Comparator is 'not consistent with equals' (see docs for Comparator)

The easy solution - use a List with the same Comparator
0
 
heyday2004Author Commented:
Thanks for the excellent replies, I really appreciate it.
0
 
heyday2004Author Commented:
Thanks!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.