简体   繁体   中英

Custom comparison using Comparator

I have a variable which can take on three possible values ( or status ) : Available, Partial, Not Available .

Now, I have a list of these statuses. My job is to to summarize the entire result onto one status. I mean that even if one of the status in the list is Not Available , then the overall status becomes Not Available .

If all the statuses in the list are Available , while one is Partial , then the overall status is Partial .

Currently, I am doing using a very naive approach where by I have a value corresponding to each possible status and then I am comparing them one by one.

public class StringsInCusomOrder {

public static String overallStatus(ArrayList<String> statusList) throws Exception
{
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("Available", 0);
    map.put("Partial", 1);
    map.put("Not Available", 2);

    String overallstatus = "Available";
    int value = 0;

    for( String s : statusList)
    {
        if(map.get(s) > value)
        {
            overallstatus = s;
            value = map.get(s);             
        }
    }
    return overallstatus;

}
public static void main(String[] args) throws Exception {

    ArrayList<String> statusList = new ArrayList<String>();
    statusList.add("Available");
    statusList.add("Partial");
    statusList.add("Not Available");
    statusList.add("Partial");

    System.out.println(overallStatus(statusList));
}
}

I wondered if there is a better approach of doing this ? Can I use comparator to do this custom comparison ?

I would suggest using an Enum for the status values instead of a String.
You can then simply use Collections.min() to get the lowest value of your EnumSet like so:

public Enum Status {
  NOT_AVAILABLE,
  PARTIAL,
  AVAILABLE
}

public Status overallStatus(EnumSet<Status> statusList) {
  return Collections.min(statusList);
}

If you want to use the contains operation then it is worth noting that this is O(n) for a List but only O(1) for a Set so a cleaner approach would be:

public String getStatus(final Collection<String> in) {
    final Set<String> set = new HashSet<>(in);
    if (set.contains("Not Available")) {
        return "Not Available";
    }
    if (set.contains("Partial")) {
        return "Partial";
    }
    return "Available";
}

I prefer my enum based approach however as this is a fast road to a tyranny of if s.

public String listStatus(List<String> toCheck) {

    if (toCheck.contains("Not available")) {
        return "Not available";
    } else if (toCheck.contains("Partial")) {
        return "Partial";
    } else {
        return "Available";
    }
}

Try this one

   public static String overallStatus(ArrayList<String> statusList)
    {
        if(statusList.contains("Not Available"))
          return "Not Available";
        else if(statusList.contains("Partial"))
               return "Not Available";
       return "Available";
    }

I would use an enum class:

public enum Status {

    AVAILABLE("Available"),
    PARTIAL("Partial"),
    NOT_AVAILABLE("Not Available");
    private static final Map<String, Status> LOOKUP;

    static {
        LOOKUP = new HashMap<>();
        for (final Status s : values()) {
            LOOKUP.put(s.key, s);
        }
    }

    public static Status lookup(final String status) {
        final Status s = LOOKUP.get(status);
        if (status == null) {
            throw new IllegalArgumentException(status + " not a vaild status.");
        }
        return s;
    }

    public static Status getStatus(final Iterable<String> input) {
        final SortedSet<Status> transformed = new TreeSet<>();
        for (final String in : input) {
            transformed.add(lookup(in));
        }
        return transformed.last();
    }

    //Alternative method not using a SortedSet and getting the max on the fly
    public static Status getStatus(final Iterable<String> input) {
        Status max = Status.AVAILABLE;
        for (final String in : input) {
            final Status curr = lookup(in);
            if (curr.compareTo(max) > 0) {
                max = curr;
            }
        }
        return max;
    }

    private final String key;

    private Status(String key) {
        this.key = key;
    }
}

The enum encapsulates the status codes. There are static methods to turn your String status into an instance of the enum .

In order to get the current Status the Iterable<String> is transformed into a SortedSet<Status> . Enums are by default sorted into declaration order so all that needs to be done then is a call to transformed.last() to find the "highest" status.

I think this approach is better an String comparison as it encapsulates your Status and associated methods into a class.

I would recommend that you use the Status object rather than a String elsewhere in your code as this then adds type safety as well.

You can use a better approach:

String currentStatus = statusList.get(0);
for( String s : statusList) {
    if (s.equalsIgnoreCase("Partial"))
        currentStatus = s;
    else if (s.equalsIgnoreCase("NotAvailable");
        return s;
}
return currentStatus;

This way you scan the list JUST ONE TIME. If all the states are Available you final state is Available. If at least one of the element is Partial and there are not NotAvailable your final state is Partial. If there is just one NotAvailable state your final state is NotAvailable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM