Code Newbie
News     Forums     Search     Members     Sign Up    

My Code Newbie
Username

Password

Articles/Snippets
ASP Classic
ASP.NET
C
C#
C++
HTML / CSS
Java
Javascript
Linux / BSD
Perl
PHP
Python
Ruby
SQL
VB 6
VB.NET

C.N. Friends
  Planet Rome

Link to Us!
Code Newbie
  Code Newbie
    forums

Go Back   Code Forums > Application and Web Development > Java

Reply
 
LinkBack Thread Tools Display Modes
Old 07-13-2004, 10:32 AM   #1 (permalink)
sde
Moderator
 
sde's Avatar
 
Join Date: May 2002
Location: us.ca
Posts: 4,530
sde is on a distinguished road
sorting objects arrays by object properties

does anyone know a good article or an easy way to sort an array of objects by any string parameter of that object?

for example, a user object:

user.firstName
user.lastName

if i have an array of users, i want to be able to sort that array based on either first or last name.
__________________
Mike
sde is offline   Reply With Quote
Old 07-13-2004, 10:53 AM   #2 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
Create a Comparator object, and use Arrays.sort().
__________________
GitS
Belisarius is offline   Reply With Quote
Old 07-14-2004, 07:47 PM   #3 (permalink)
technobard
Centurion Nova Prime
 
technobard's Avatar
 
Join Date: May 2002
Location: Oak Park, IL (USA)
Posts: 287
technobard is on a distinguished road
I think Belisarius and I have been on opposite sides of this discussion before...or maybe it was someone else. I like the Comparable interface better than comparators. (see Sorting Objects Tutorial)

The problem is that whatever you do, you will have to define how to do the sort on a particular object variable beforehand. Being able to pick "any" string parameter is tough.

In my tutorial, I call a method called strangeFactor(). One approach would be to use a static variable to indicate which object variable to sort by and then have your code branch on that value. If you stick that code in a method like strangeFactor(), you can keep the actual compare simple.

If you're feeling really adventurous, I suspect you can make the solution more generic by using the reflection api to get the field/variable names and values for a given object. You'll still want to use a static class variable to set the sort field name. If the datatypes are something other than String, you'll also have to look at the datatype of the sort field and have code to handle the differences in comparisons, if any.

Belisarius will probably point out that if you use reflection to go this generic, a comparator is an excellent choice. Oh, well.
technobard is offline   Reply With Quote
Old 07-14-2004, 08:21 PM   #4 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
I've actually done it both ways, and I found I prefer the Comparator route because Comparable always seemed to me the preferred way to define the natural ordering of the objects, whereas Comparator is best when you want to sort objects by different criteria. Setting a static field for the purposes of sorting is a bad idea, IMHO, as it will cause headaches in a multi-threaded environment. Say you want to sort one array of objects in manner A in thread #1, and a different array in manner B in thread #2. Doing it statically really screws you.

I generally just create an anonymous Comparator class for the job. It's atomic, so no problems with multi-threaded, plus you don't need to worry about altering the original class should you want to sort by a different criteria in the future.
__________________
GitS
Belisarius is offline   Reply With Quote
Old 07-14-2004, 08:22 PM   #5 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
Oh, and I've never used reflection. I honestly don't know that much about it. The entire thing strikes me a really cool hack, but something that generally can (and probably should) be avoided.
__________________
GitS
Belisarius is offline   Reply With Quote
Old 07-14-2004, 08:55 PM   #6 (permalink)
sde
Moderator
 
sde's Avatar
 
Join Date: May 2002
Location: us.ca
Posts: 4,530
sde is on a distinguished road
omg, i didn't even look at our java tutorials .. i guess i'll spend time there too. i haven't even tried this stuff yet .. i've been side tracked, but i will work with them tomorrow.

thanks
__________________
Mike
sde is offline   Reply With Quote
Old 07-14-2004, 09:09 PM   #7 (permalink)
technobard
Centurion Nova Prime
 
technobard's Avatar
 
Join Date: May 2002
Location: Oak Park, IL (USA)
Posts: 287
technobard is on a distinguished road
My biggest issue with Comparator has always been that it doesn't feel as object oriented as implementing an interface. I like having the method for comparing the objects as part of the object. In the end, I know it's just my personal preference.

Yeah, even Sun will recommend that you don't use reflection unless you absolutely have to. The sort on any string question though makes me want to write it just to see how well it works.

The use of static variables (IMO) have their place. There are certainly limitations and gotchas that you rightly pointed out. In this case, I'm only talking about holding the name of the field that will be sorted on, not the actual values, but under the right circumstances, I suppose that could be an issue. You could just as easily hold the "sort field name" outside the class, it just appeals to my sense of order when using Comparable that the value be accessible within the class/object.


Later.
technobard is offline   Reply With Quote
Old 07-15-2004, 03:55 AM   #8 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
The only way I can think of to hold it outside the class is with a singleton, at which point you've just really raised the complexity just to sort.

Like I said, I use Comparable to enforce natural ordering, but if I want to sort in more than one way, I use Comparator. Granted, it is a bit of a hack designed to pass a method to a method, but it simply is the way to go when using different sort methods on the same object.
__________________
GitS
Belisarius is offline   Reply With Quote
Old 07-15-2004, 04:30 PM   #9 (permalink)
sde
Moderator
 
sde's Avatar
 
Join Date: May 2002
Location: us.ca
Posts: 4,530
sde is on a distinguished road
i ended up getting the comparator object to work because it was the easiest.

thanks!
__________________
Mike
sde is offline   Reply With Quote
Old 07-21-2004, 03:09 PM   #10 (permalink)
technobard
Centurion Nova Prime
 
technobard's Avatar
 
Join Date: May 2002
Location: Oak Park, IL (USA)
Posts: 287
technobard is on a distinguished road
Generic String Comparator

I spent the last few days in an Oracle Database Security class. I had a little free time in between and decided to see how much work would be required to do this sort thing. Surprisingly, not as much as I thought.

For anyone interested, StringComparator allows you to sort on any public member variable or method. I made some choices based on my own preferences here that could easily be coded a different way. (See code below)

The requirements are simple.
StringComparator strComp = new StringComparator(Object sample, String sortbyme, String sorttype);
creates the Comparator object. "sample" is an object of the type that will be sorted. "sortbyme" is the case-sensitive name of the field or method that will be used to drive the sort. "sorttype" is either "string" or "method" indicating the obvious. The fields and/or methods used for sorting must evaluate as a String. (I have to add code to prevent other types.)

The code could use a few checks here and there, but works fairly well so far in limited testing. I would appreciate any comments, suggestions, etc.

Code:
import java.util.*;
import java.lang.reflect.*;

public class StringComparator implements Comparator{
    Field[] publicFields;
    Method[] theMethods;
    Class[] parameterTypes;
    Vector methvec;
    Vector fieldvec;
    String sortBy = "";
    String sortType = "string";
    boolean nameMatch = true;
    int methodIndex = 0;
    
    /** Creates a new instance of StringComparator */
    public StringComparator() {
    }
        
    public StringComparator(Object sample, String sortbyme, String sorttype) {
        sortBy = sortbyme;
        sortType = sorttype;
        Class c0 = sample.getClass();  //get sample class
        methvec = new Vector();
        fieldvec = new Vector();
        String tempName = "";
        
        theMethods = c0.getMethods();
        for (int i = 0; i < theMethods.length; i++) {
            tempName = theMethods[i].getName();
            methvec.add(tempName);
            if (tempName.equals(sortBy)) {
                parameterTypes = theMethods[i].getParameterTypes();
                methodIndex = i;
            }
        }
        
        publicFields = c0.getFields();
        for (int i = 0; i < publicFields.length; i++) {
            fieldvec.add(publicFields[i].getName());
        }
        
        if (sortType.equals("string")) {
            nameMatch = fieldvec.contains(sortBy);
        }
        
        if (sortType.equals("method")) {
            nameMatch = methvec.contains(sortBy);
        }
        
    }  // end of constructor
    
    public boolean isValid() {
        return nameMatch;
    }
    
    public void setSortBy(String sortbyme) {
        sortBy = sortbyme;
    }
    
    public String getSortBy() {
        return sortBy;
    }
    
    public int compare(Object obj1, Object obj2) {
        Class c1 = obj1.getClass();
        Class c2 = obj2.getClass();
        String str1val = "";
        String str2val = "";
        
        if (sortType.equals("string")) {
            try {
                Field str1 = c1.getField(getSortBy());
                Field str2 = c2.getField(getSortBy());
                str1val = (String) str1.get(obj1);
                str2val = (String) str2.get(obj2);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
        
        if (sortType.equals("method")) {
            
            try {
                str1val = (String) theMethods[methodIndex].invoke(obj1, parameterTypes);
                str2val = (String) theMethods[methodIndex].invoke(obj2, parameterTypes);
            } catch (IllegalAccessException e) {
                System.out.println(e);
            } catch (InvocationTargetException e) {
                System.out.println(e);
            }
        }
        
        return str1val.compareTo(str2val);
    }
}
I was concerned about overhead. So one of the choices I made was to pre-populate a lot of the metadata from the reflection api during construction of the object. This moved repetitive calls out of the actual compare() method. I will probably do some timing comparisons on say a 10,000 object array to see how it stacks up to a normal Comparator for the same task...when I get some more time anyway.
technobard is offline   Reply With Quote
Old 07-21-2004, 05:06 PM   #11 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
Not to put down what is actually a pretty cool bit of coding, but there's an inheirent problem for those looking to implement this; you're moving possible compilation errors to runtime. Whereas if you mistype a field-name if you accessed it through a field or method it wouldn't compile, with this you wouldn't know a problem exists until you attempted to run the code that contained the error.
__________________
GitS
Belisarius is offline   Reply With Quote
Old 07-21-2004, 07:10 PM   #12 (permalink)
technobard
Centurion Nova Prime
 
technobard's Avatar
 
Join Date: May 2002
Location: Oak Park, IL (USA)
Posts: 287
technobard is on a distinguished road
Yep. A very valid point. Unfortunately, that's the trade off when using a metadata driven solution (in this case reflection). I was worried about that when I added the isValid() method as a runtime check to return whether the name passed in matched any of the public fields/methods in the class definition, but it's still runtime. My biggest concern is non-string fields and methods. I can get around that as well by checking class and return types.

In my sample program, I actually use it this way:
Code:
StringComparator strcomp = new StringComparator(p1, "getFullName", "method");
if (strcomp.isValid()) {
    Collections.sort(testvec, strcomp);
}else{
//do something useful here
}
I just had another thought. Since the valid list of field and method names is stored within the Comparator object, I could make those items available so that they could populate a drop down in a GUI or something. That would eliminate typos as a source of error, at least in that type of app.

Thanks for the feedback! This is mostly an exercise in seeing if it could be done, but I might just use it.
technobard is offline   Reply With Quote
Old 07-27-2004, 12:09 PM   #13 (permalink)
technobard
Centurion Nova Prime
 
technobard's Avatar
 
Join Date: May 2002
Location: Oak Park, IL (USA)
Posts: 287
technobard is on a distinguished road
A quick update. I did some speed tests creating Vectors of Person objects of various sizes and sorting the Vectors using a traditional Comparator vs the generic StringComparator.

The Un-Scientific Results: Up to about 10,000 objects, sort speed is fairly close between the two approaches. The traditional route is usually faster, but not always and not by much. Go above 10,000 objects and you see a roughly 10 - 12% overhead associated with the generic method. For example, I ran a sort of 100,000 objects:
-- Traditional ==> 4.0 secs
-- Generic ==> 4.5 secs

Repeated tests varied over a range, but were generally within 10 - 12%.
Your mileage may vary.
technobard is offline   Reply With Quote
Old 07-27-2004, 04:05 PM   #14 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
Quote:
I like having the method for comparing the objects as part of the object.
This by now quite a standard in OO techniques. No need to know Java, C++ or any OO-paradigm-able language.
By using this method you'll be able to make more generic classes, and therefore loosening up the coupling between the various classes.
A recent member (grid) in the C++ forum accepted a challenge. The issue I just mentioned will play a role. After this weekend (most likely) I'll demonstrate the code.

Another method one could use is to be found in design patterns. If you are familiar with the template method pattern and/or strategy pattern, then see if it fits sde's needs. It will be one heck of elegant and OO-based solution. Note that the sorting machine doesn't have to know what it is sorting.
__________________
Valmont is offline   Reply With Quote
Old 07-27-2004, 07:44 PM   #15 (permalink)
Belisarius
Java fanboy
 
Belisarius's Avatar
 
Join Date: Aug 2003
Posts: 1,175
Belisarius is on a distinguished road
I guess you lost me Val. I'm not sure just how much more abstract you can get than a Comparable or Comparator in Java.
__________________
GitS
Belisarius is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
sorting multi dimensional arrays sde PHP 4 10-01-2003 06:52 PM


All times are GMT -8. The time now is 12:28 AM.


Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO 3.0.0 RC8 ©2007, Crawlability, Inc.





Copyright © 2000-2008, Milano Interactive
Web Hosting provided by Portal 360 Web Hosting