Greg Valvo
Greg Valvo

Reputation: 1099

Sort List Using Different Keys

Extreme newbie here struggling to get a handle on all the different ways a list may be sorted. Suppose I have a list of objects and each object has several keys that might be used for sorting in different circumstances. I got some very useful information from this thread: Java Interface Comparator static compare, using Qwerky’s first example as a model created:

class Dog {
   private String name;
   private int age;
   private int height;
   private int weight;

   Dog(String n, int a, int b, int c){
      name = n;
      age = a;
      height = b;
      weight = c;
   }
   public String getDogName(){
      return name;
   }
   public int getDogAge(){
      return age;
   } 
   public int getDogHeight(){
      return height;
   }
   public int getDogWeight(){
      return weight;
   }
}

class DogComparator1 implements Comparator<Dog> {
   @Override
   public int compare(Dog d, Dog d1){
      return d.getDogAge() - d1.getDogAge();
   }
}
   class DogComparator2 implements Comparator<Dog> {
   @Override
   public int compare(Dog d, Dog d1){
      return d.getDogHeight() - d1.getDogHeight();
   }
}
   class DogComparator3 implements Comparator<Dog> { 
   @Override
   public int compare(Dog d, Dog d1){
      return d.getDogWeight() - d1.getDogWeight();
   }
}


public class Example{

   public static void main(String args[]){
      // Creat list of dog objects
      List<Dog> dogList = new ArrayList<>();

      // Add a buch of dogs to the list here
               .
               .

      // Create the Comparators
      DogComparator1 compare1 = new DogComparator1();
      DogComparator2 compare2 = new DogComparator2();
      DogComparator3 compare3 = new DogComparator3();


      // Sort the list using Comparators
      Collections.sort(list, compare1);  // Sort by age     
      Collections.sort(list, compare2);  // Sort by height
      Collections.sort(list, compare3);  // Sort by weight          
   }
}

But, this just doesn’t seem right. I think I want to pull the comparator definitions “inside” the Dog class to nicely encapsulate them. But, can’t figure out how to do it.

Am I on the right track? If so, help with the proper syntax would be greatly appreciated.

Upvotes: 4

Views: 430

Answers (3)

Alex - GlassEditor.com
Alex - GlassEditor.com

Reputation: 15497

Consider using Comparator#comparingInt to create your comparators:

  list.sort(Comparator.comparingInt(Dog::getDogAge));  // Sort by age     
  list.sort(Comparator.comparingInt(Dog::getDogHeight));  // Sort by height
  list.sort(Comparator.comparingInt(Dog::getDogWeight));  // Sort by weight  

This way saves you from having to write your own Comparator<Dog> implementations as you are doing now and it's easy to see exactly what is being compared.

Since the Comparators are already only using the public methods of Dog and they are this short there is not much reason to encapsulate them inside of Dog.

Upvotes: 1

theadam
theadam

Reputation: 4051

I do not think it is a responsibility of the Dog class to know the different ways in which you can compare it. You can, however create those comparators as inner classes if that would feel more "encapsulated" for you (and even make this class private).

I.e. you could, inside Dog class, do this:

public class Dog {

(...)

    public Comparator<Dog> getAgeComparator() {
        return new AgeComparator();
    }

    private static class AgeComparator implements Comparator<Dog> {
       @Override
       public int compare(Dog d, Dog d1){
          return d.getDogAge() - d1.getDogAge();
       }
}

Or, instead of creating a getAgeComparator() method expose those comparators as:

public static final Comparator<Dog> AGE_COMPARATOR = new AgeComparator();

You could also just create the anonymous implementation there. The way you are doing this though is correct. I would not make Dog aware of how it can be compared. You could easily have those implementations live elsewhere, or even create anonymous class implementations on the fly if you do not reuse them. What you are trying to achieve is a wrong understanding of encapsulation.

You can create a default comparator though by making Dog class implement Comparable<Dog>:

public class Dog implements Comparable<Dog> {

    public int compareTo(Dog dog) {
        return AGE_COMPARATOR.compare(this, dog); // or inline comparison here
    }

    (...)
}

Upvotes: 3

vikingsteve
vikingsteve

Reputation: 40378

You are definitely on the right track.

I would just add this inside your Dog class:

public static final Comparator<Dog> COMPARE_BY_AGE = new Comparator<Dog>() {
    @Override
    public int compare(Dog d, Dog d1) {
        return d.getDogAge() - d1.getDogAge();
    }
};

public static final Comparator<Dog> COMPARE_BY_HEIGHT = new Comparator<Dog>() {
    @Override
    public int compare(Dog d, Dog d1) {
        return d.getDogHeight() - d1.getDogHeight();
    }
};

public static final Comparator<Dog> COMPARE_BY_WEIGHT = new Comparator<Dog>() {
    @Override
    public int compare(Dog d, Dog d1) {
        return d.getDogWeight() - d1.getDogWeight();
    }
};

And then the usage is like follows:

    // Sort the list using Comparators
    Collections.sort(list, Dog.COMPARE_BY_AGE);  // Sort by age
    Collections.sort(list, Dog.COMPARE_BY_HEIGHT);  // Sort by height
    Collections.sort(list, Dog.COMPARE_BY_WEIGHT);  // Sort by weight

Upvotes: 5

Related Questions