Search code examples
javasortingcollectionscomparatorcomparable

How do you sort a vector of custom objects by attribute, that is a Map of other custom objects?


The hierarchy goes like:

  1. Class Drug - it has drugs name and price.

  2. Class Supplier - it holds a Map of <Drug drug, int quantity>.

Now I have a Vector<Suppliers> (which holds, let's say 5 suppliers) and I want to sort in descending order of the quantity of an user inputted drugs name.

Is this even possible? I've tried using comparators but I can't get it working.

Ok, what I tried to do is put only the suppliers, that have the passed as parameter drug in a new List and THEN perform the sort on the list. I think this is a good workaround, but then I need to change the 'sort' somehow.

Here is the method where I iterate over all suppliers and check if they have the drug, if they do -> add them in the new list and try to sort it in the end.

public List<Supplier> getSortedSuppliersByQuantity(Drug drug) {
    List <Supplier> sortedSuppliers = new ArrayList <Supplier>();
    for(Supplier s : suppliers) {
        for(Entry<Drug, Integer> entry : s.getListOfDrugs().entrySet()) {
            if(entry.getKey().getDrugsName().equals(drug.getDrugsName()));
                sortedSuppliers.add(s);
        }
    }
    sort(drug, sortedSuppliers);
    return sortedSuppliers;
}

Problem is that I get the NullPointerException at the sort line.

I think that the sort method, that you suggested, needs to be changed to somehow act on the new List and not the old one?

Stacktrace for @Andrew

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Unknown Source)
    at java.util.Optional.<init>(Unknown Source)
    at java.util.Optional.of(Unknown Source)
    at myapp.Supplier.getKeyExtractor(Supplier.java:22)
    at myapp.Orders.lambda$0(Orders.java:89)
    at java.util.Comparator.lambda$comparing$77a9974f$1(Unknown Source)
    at java.util.TimSort.countRunAndMakeAscending(Unknown Source)
    at java.util.TimSort.sort(Unknown Source)
    at java.util.Arrays.sort(Unknown Source)
    at java.util.ArrayList.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at myapp.Orders.sort(Orders.java:89)
    at myapp.Orders.getSortedSuppliersByQuantity(Orders.java:106)
    at myapp.main.main(main.java:22)

Solution

  • The following comparator performs the same actions like yours does, but in a shorter Java 8 form:

    public void sort(Drug drug, Vector<Supplier> suppliers) {
        Collections.sort(suppliers, Comparator.comparing(s -> s.getDrugs().get(drug)));
    }
    

    The problem is possible when either a map doesn't contain the given drag or a supplier doesn't have drugs to trade (NullPointerException).

    How to solve? Add null checks or pass valid arguments to the method.


    EDIT:

    I wrote the getKeyExtractor method which will throw an exception with a detailed message (if something bad happens) to know what is going on:

    public void sort(Drug drug, Vector<Supplier> suppliers) {
        Collections.sort(suppliers, Comparator.comparing(s -> Supplier.getKeyExtractor(s, drug)));
    }
    
    public static Integer getKeyExtractor(Supplier supplier, Drug drug) {
        return Optional.ofNullable(Optional.ofNullable(supplier.getDrugs())
                                   .orElseThrow(() -> new IllegalArgumentException("drugs is null")).get(drug))
                       .orElseThrow(() -> new IllegalArgumentException("the drug couldn't be found"));
    }
    

    P.S.

    I would suggest you to use a List in a pair with the ArrayList here instead of a Vector.