Using Comparable to compare generic variables

For one of the Homeworks in my class, we have a collection of a class titled Pair and we need to sort it in ascending order based on the value of the key.

I could apply this if the keys were strings or integers, but how do I write code that would compare my items when they're Generic as seen below?

The professor in my class explained what to do for integers or strings but when my variables are generic I'm at a complete loss.

Below are copies of the relevant parts of my code.

import java.util.*;


public class Utils {

    public static<K extends Comparable<K>, V> Collection<Pair<K,V>> sortPairCollection(Collection <Pair<K,V>> col){
        ArrayList <Pair<K,V>> list = new ArrayList<>();
        //Code to compare

        return list;
    }

    public static void main(String[] args) {
        ArrayList <Pair<String,Integer>> list = new ArrayList<>();
        Pair<String, Integer> e = new Pair<>("One", 1);
        list.add(e);
        Pair<String, Integer> f = new Pair<>("Two", 2);
        list.add(f);

        Utils help = new Utils();
        help.sortPairCollection(list);
    }
}

This second part here is the code for my Pair class. import java.io.Serializable; import java.util.Objects;

public class Pair <K,V> extends Object implements Serializable, Cloneable{


    public Pair(K k, V v){
       this.k = k;
       this.v = v;
    }


    public K k(){
       return k;
    }


    public V v(){
       return v;
    }


   /*
   ... //irrelevant data omitted
   */

   private final K k;
   private final V v;
}

3 answers

  • answered 2018-11-08 06:29 Meini

    Option 1.Use a Comparator

    public class Cmp<K extends Comparable<K>, V> implements Comparator<Pair<K, V>> {
       @Override
       public int compare(Pair<K, V> o1, Pair<K, V> o2) {
          return o1.k.compareTo(o2.k);
       }
    }
    
    public class Utils {
        public static <K extends Comparable<K>, V> Collection<Pair<K, V>> sortPairCollection(
                Collection<Pair<K, V>> col) {
            ArrayList<Pair<K, V>> list = new ArrayList<>();
            Collections.sort(list, new Cmp<>());
            return list;
        }
    }
    

    Option 2. Implement Comparable

    public class Pair<K extends Comparable<K>, V> implements Comparable<Pair<K, V>> {
        private K k;
        private V v;
        @Override
        public int compareTo(Pair<K, V> o) {
            return k.compareTo(o.k);
        }
    }
    public class Utils {
        public static <K extends Comparable<K>, V> Collection<Pair<K, V>> sortPairCollection(Collection<Pair<K, V>> col) {
            ArrayList<Pair<K, V>> list = new ArrayList<>();
            Collections.sort(list);
            return list;
        }
    }
    

    Or just

    public class Utils {
        public static <K extends Comparable<K>, V> Collection<Pair<K, V>> sortPairCollection(Collection<Pair<K, V>> col) {
            ArrayList<Pair<K, V>> list = new ArrayList<>();
            Collections.sort(list, (p, o) -> p.k.compareTo(o.k));
            return list;
        }
    }
    

    You don't have do create an instance for your static method btw. just invoke

    Utils.sortPairCollection(list);
    

  • answered 2018-11-08 06:39 Siddhesh Rane

    import java.util.*;
    public class Utils {
    
    public static <K extends Comparable<K>, V> Collection<Pair<K, V>> sortPairCollection(Collection<Pair<K, V>> col) {
        ArrayList<Pair<K, V>> list = new ArrayList<>(col);
        //Code to compare
        list.sort(Comparator.comparing(Pair::k)); //this is the only change needed
        return list;
    }
    
    public static void main(String[] args) {
        List<Pair<String, Integer>> listas = Arrays.asList(
            new Pair<>("One", 1),
            new Pair<>("Two", 2));
        System.out.println(Utils.sortPairCollection(listas));
    
    }
    

    What we did here is that we extracted an already Camparable key from our Pair and passed that to the Comparator class static method which will generate a custom Comparator for our Pair class. See the Javadoc for Comaparator here

  • answered 2018-11-08 12:13 Syed Ahmed Jamil

    If you are a clean and elegant code lover like me then you can use the power of lambda expression to create an anonymous function (function with no name) on the spot that handles the comparison logic. Technically it is the same as when using Comparable or Comparator functional interfaces but using lambdas you don't have to write boilerplate code by creating classes just for one function. Instead you create that function on spot int the form of lambda expression, java handles the rest. Lambda expressions were introduced in Java 8.

    Lambda expression have the form ( <parameters> ) -> { //body of the function }

    public class Pair<K ,V extends Comparable<V>> {
    private K k;
    private V v;
    public Pair(K k, V v){
       this.k = k;
       this.v = v;
    }
    public K k(){
       return k;
    }
    public V v(){
       return v;
    }
    }
    
    
    public static void main(String[] args) {
    
            ArrayList <Pair<String,Integer>> list = new ArrayList<>();
            Pair<String, Integer> e = new Pair<>("One", 1);
            list.add(e);
            Pair<String, Integer> f = new Pair<>("Two", 2);
            list.add(f);
    
            // Second argument to this sort function is a lambda expression
            Collections.sort( list , (pair1 , pair2)->{
    
                return pair1.v().compareTo(pair2.v());
    
            });
        }
    }
    

    The data type of pair1 and pair2 will be Pair Here are some starter links about lambda expression and functional interfaces

    Functional Interfaces: https://www.geeksforgeeks.org/functional-interfaces-java/

    Lambda Expressions: https://www.geeksforgeeks.org/lambda-expressions-java-8/