Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

writing a Comparator for a compound object for binary searching

I have a class, and list of instances, that looks something like this (field names changed to protect the innocent/proprietary):

public class Bloat
{
    public long timeInMilliseconds;
    public long spaceInBytes;
    public long costInPennies;
}

public class BloatProducer
{
    final private List<Bloat> bloatList = new ArrayList<Bloat>();
    final private Random random = new Random();
    public void produceMoreBloat()
    {
       int n = bloatList.size();
       Bloat previousBloat = (n == 0) ? new Bloat() : bloatList.get(n-1);
       Bloat newBloat = new Bloat();
       newBloat.timeInMilliseconds = 
          previousBloat.timeInMilliseconds + random.nextInt(10) + 1;
       newBloat.spaceInBytes = 
          previousBloat.spaceInBytes + random.nextInt(10) + 1;
       newBloat.costInPennies = 
          previousBloat.costInPennies + random.nextInt(10) + 1;
       bloatList.add(newBloat);
    }
    /* other fields/methods */

    public boolean testMonotonicity()
    {
    Bloat previousBloat = null;
    for (Bloat thisBloat : bloatList)
            {
               if (previousBloat != null)
               {
                  if ((previousBloat.timeInMilliseconds 
                     >= thisBloat.timeInMilliseconds)
                   || (previousBloat.spaceInBytes 
                     >= thisBloat.spaceInBytes)
                   || (previousBloat.costInPennies
                     >= thisBloat.costInPennies))
                       return false;
               }
               previousBloat = thisBloat;
           }
           return true;
    }

BloatProducer bloatProducer;

The list bloatList is kept internally by BloatProducer and is maintained in such a way that it only appends new Bloat records, does not modify any of the old ones, and each of the fields is monotonically increasing, e.g. bloatProducer.testMonotonicity() would always return true.

I would like to use Collections.binarySearch(list,key,comparator) to search for the Bloat record by either the timeInMilliseconds, spaceInBytes, or costInPennies fields. (and if the number is between two records, I want to find the previous record)

What's the easiest way to write a series of 3 Comparator classes to get this to work? Do I have to use a key that is a Bloat object with dummy fields for the ones I'm not searching for?

like image 614
Jason S Avatar asked Feb 28 '23 12:02

Jason S


1 Answers

You'll need to write a separate comparator for each field you want to compare on:

public class BloatTimeComparator implements Comparator<Bloat> {
    public int compare(Bloat bloat1, Bloat bloat2) {
        if (bloat1.timeInMilliseconds > bloat2.timeInMilliseconds) {
            return 1;
        } else if (bloat1.timeInMilliseconds < bloat2.timeInMilliseconds) {
            return -1;
        } else {
            return 0;
        }
    }
}

And so on for each property in Bloat you want to compare on (you'll need to create a comparator class for each). Then use the Collections helper method:

Collections.binarySearch(bloatList,  bloatObjectToFind, 
    new BloatTimeComparator());

From the Java documentation for the binarySearch method, the return value will be:

the index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size() if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.

Which is the index you specified that you wanted.

like image 190
Peter Avatar answered Apr 28 '23 20:04

Peter