We help IT Professionals succeed at work.

Question about Comparators

Gregg
Gregg asked
on
Medium Priority
390 Views
Last Modified: 2013-11-23
Is it possible to use the same Comparator class to compare any attribute of type String?

Example:
I have a person object with attributes:

String: firstName, lastName, address1, city, state, zip

Integer: personID

Id like to be able to sort by any one attribute (firstname, lastname, and city for example).

Do i create a comparator class for each? FirstNameComparator, LastNameComparator, CityComparator, etc...

Or is it possible to create a comparator that accepts string? Is that what generics does?

Thanks!
Comment
Watch Question

Awarded 2011
Awarded 2011

Commented:
You can have parameter set up in the constructor of yur comparator which will speify wjich attribute to us in comparison
Awarded 2011
Awarded 2011

Commented:
Just include instance varaible say flag into your comparator abnd set it up n the constructor and then use this flag in compae() methiod to determine which field to use for comparison
Awarded 2011
Awarded 2011

Commented:
Public class MyComp {
Int flag;
Public MyComp(int I){
This.flag = I;
}

Public int compare(Person p1, Person p2){
If(flag == 0){
// do comnparison on name
} Else if flag == 1 {
// do comparison on age
}
//etc
}

And theen you just say
Collections.sort(arraylis, new MyComp(0));
In anoteer case yiu crerate MyComp(1) etc.

Sorry for wrong case of the characcters in the coe above - this is mobile tricks
 Ffrom my pohone
- hope you get the idxea

Awarded 2011
Awarded 2011

Commented:
You sure can have compaator on Strings but in this acse it will be more convenient to have comparator on Person but with this flag parameter
CERTIFIED EXPERT
Top Expert 2016

Commented:
You can use reflection to sort on any field you name. Here's a simple example:

https://suif.stanford.edu/svn/prpl/prpl_core/trunk/src/java/edu/stanford/prpl/common/GenComparator.java
Awarded 2011
Awarded 2011
Commented:
Ok , now I typed it cleaner
and with this comoarator below
you can sort uyour lists

with

Collections.sort(your_list, new MyComp5(0));

or Collectons.sort(your_list, new MyComp5(1)),

etc.



class Person {

 String lastName;
 int age;
 String city;

public String getName(){
    return lastName;
}
public String getCity() {
    return city;
}

public int getAge() {
    return age;

}


}

class MyComp5  implements Comparator<Person> {
    int flag;

public MyComp5(int i){
    flag = i;
}

public int compare(Person p1, Person p2){
    if(flag == 0) {
        return p1.getName().compareTo(p2.getName());
    } else if (flag == 1){
        if(p1.getAge() <p2.getAge()) return -1;
        else   if(p1.getAge() > p2.getAge()) return 1;
        else return 0;
    }  else if (flag == 2){
        return p1.getCity().compareTo(p2.getCity());
    }
       return 0; 

}


}

Open in new window

You can certainly use reflection as CEHJ suggested
but sometimes it can be overkill, and reflection takes its toll in the sense
of performnce, so if that is important - for long listst where you'll have many comarisons,
it is better to do it in more conventional way





Author

Commented:
Thanks guys! Im looking into both options.

CEHJ: I find your example interesting. Is it a generally accepted approach? That would allow for me to use the Comparator for many custom classes and not have to recreate code like i did below - would you agree?

And since my last post, i put this together. I havent tested yet - it follows for_yans thought process. for_yan, what do you think?

package util.sort;

import java.util.Comparator;
import logic.Contact;

/**
 * This class represents a Comparator object for sorting
 * two contacts. The attribute to sort by is passed in
 * via constructor.
 */
public class SortBy implements Comparator<Contact> {

	//Attribute that holds selection passed in constructor.
	private SortByField whichToSort;
	
	//Constructor
	/**
	 * Constructor that accepts one parameter. 
	 * @param whichToSort This is the value to sort by.
	 */
	public SortBy(SortByField whichToSort)
	{
		this.whichToSort = whichToSort;
	}

	/**
	 * Compare two contacts based on the Sort Field Enum Selection 
	 * passed into Constructor.
	 */
	@Override
	public int compare(Contact contact1, Contact contact2) {
		
		int result = 0;
		
		if(this.whichToSort.equals(SortByField.FIRST_NAME))
		{
			result = contact1.getFirstName().compareTo(contact2.getFirstName());
		}
		else if(this.whichToSort.equals(SortByField.LAST_NAME))
		{
			result = contact1.getLastName().compareTo(contact2.getLastName());
		}
		else if(this.whichToSort.equals(SortByField.CITY))
		{
			result = contact1.getCity().compareTo(contact2.getCity());
		}
		else if(this.whichToSort.equals(SortByField.STATE))
		{
			result = contact1.getState().compareTo(contact2.getState());
		}
		else if(this.whichToSort.equals(SortByField.ZIP))
		{
			result = contact1.getZip().compareTo(contact2.getZip());
		}
		
		return result; 
	}
	
	/**
	 * This inner class represents the options for sorting.
	 */
	public enum SortByField {
		
		//Enumeration Options
		FIRST_NAME(0), 
		LAST_NAME(1),
		CITY(2),
		STATE(3),
		ZIP(4);
		
		
		private int sortByValue;
		
		//Constructor
		private SortByField(int sortByValue) 
		{
			this.sortByValue = sortByValue;
		}
		
		/**
		 * Returns the sortByField as integer.
		 * Options include: FIRST_NAME, LAST_NAME, CITY, STATE, ZIP
		 * @return sortByValue - the value representing the ENUM selection.
		 */
		public int getSortByField()
		{
			return sortByValue;
		}

	}

}

Open in new window

Awarded 2011
Awarded 2011

Commented:
yes, I think it should work, the way you wrote it
Awarded 2011
Awarded 2011

Commented:
In gneral the decision  will depend on your situation - if you really want to
sort all the time different classes by different fields, on the other hand sorting is defintely
not a bottleneck in your appliaction (that would be the case if you have moderatlye sized lists, which say you
first get from database - sure interaction with databse will be more of the bottleneck) then generality which you can
achivee with reflection would be of value

I still think in most of the cases folks are using more conventional way rather than doing reflection,
but if you have vwery many classes and in need of writing new comparator every other day  - then reflection will be a good choice
CERTIFIED EXPERT
Top Expert 2016
Commented:
If you're going to do it the more conventional way, you can make the code more readable and maintainable by doing it like the below.

You can sort with (eg):

Collections.sort(people, new PersonComparator(PersonComparator.SortBy.Age));
class PersonComparator implements Comparator<Person> {
    private SortBy sortField;

    public PersonComparator(SortBy sortField) {
        this.sortField = sortField;
    }

    public int compare(Person p1, Person p2) {
        int result = 0;

        switch (sortField) {
            case LastName:
                result = p1.getName().compareTo(p2.getName());

                break;

            case Age:
                result = p1.getAge() - p2.getAge();

                break;

            case City:
                result = p1.getCity().compareTo(p2.getCity());
        }

        return result;
    }
    enum SortBy {LastName,
        Age,
        City;
    }
}

Open in new window

CERTIFIED EXPERT
Top Expert 2016

Commented:
>>CEHJ: I find your example interesting. Is it a generally accepted approach?

Well there exist for example google classes that use reflection. The overhead of reflection is generally exaggerated, but you should always benchmark these things rather than relying on anecdote

Author

Commented:
I do like the reflection example and i will explore that in more detail. I am happy to have the conventional approach working. Thank you both for taking the time to help me learn this.
CERTIFIED EXPERT
Top Expert 2016

Commented:
:)

Explore More ContentExplore courses, solutions, and other research materials related to this topic.