简体   繁体   中英

How to write sorting and grouping algorithm shorter?

I have written a sorting algorithm but in an ugly way and I am wondering if someone would help me how to improve it (make code shorter)? I am limited to use Java 6.

  1. Group elements in multimap
  2. Sort Elements in Groups (by date asc). Put sorted groups into list of lists.
  3. Sort list by date asc of the first element in the group.
  4. Flatten the list of list to a list of elements.

The sorting is implemented in the sort method.

Input List: 32102421 <-sorted by this values ascending 11231455 <-grouped by this values

Output List: 01122234 32551114

    public static void main(String[] args) {
    System.out.println(sort(prepareTestData1()));
}

public static List<ElementToSort> sort(List<ElementToSort> testData) {
    ListMultimap<Integer, ElementToSort> voyagesMap = LinkedListMultimap.create();
    // 1. Group elements in multimap
    for (ElementToSort voyage : testData) {
        voyagesMap.put(voyage.getPio(), voyage);
    }

    // 2. Sort Elements in Groups (by date asc). Put sorted groups into list of lists.
    List<List<ElementToSort>> preSortedList = new ArrayList<List<ElementToSort>>();
    for (Integer key : voyagesMap.keySet()) {
        List<ElementToSort> voyages = voyagesMap.get(key);
        preSortedList.add(Ordering.from(getDateComparator()).sortedCopy(voyages));
    }

    // 3. Sort list by date asc of the first element in the group.
    preSortedList = Ordering.from(getDateListComparator()).sortedCopy(preSortedList);

    // 4. Flatten the list of list to a list of elements.
    List<ElementToSort> returnList = new ArrayList<ElementToSort>();
    for (List<ElementToSort> packOfVoyages : preSortedList) {
        returnList.addAll(packOfVoyages);
    }

    return returnList;
}

public static List<ElementToSort> prepareTestData1() {
    return Arrays.asList(new ElementToSort(3, 1), new ElementToSort(2, 1), new ElementToSort(1, 2), new ElementToSort(0, 3), new ElementToSort(2, 1), new ElementToSort(4, 4), new ElementToSort(2, 5), new ElementToSort(1, 5));
}

public static Comparator<ElementToSort> getDateComparator() {

    return new Comparator<ElementToSort>() {

        @Override
        public int compare(ElementToSort voy1, ElementToSort voy2) {
            if (voy1.getDate() < voy2.getDate()) {
                return -1;
            } else if (voy1.getDate() > voy2.getDate()) {
                return 1;
            } else {
                return 0;
            }
        }
    };
}

public static Comparator<List<ElementToSort>> getDateListComparator() {

    return new Comparator<List<ElementToSort>>() {

        @Override
        public int compare(List<ElementToSort> voy1, List<ElementToSort> voy2) {
            if (voy1.get(0).getDate() < voy2.get(0).getDate()) {
                return -1;
            } else if (voy1.get(0).getDate() > voy2.get(0).getDate()) {
                return 1;
            } else {
                return 0;
            }
        }
    };
}

public static class ElementToSort {
    int date;
    int pio;

    @Override
    public String toString() {
        return "[" + date + ":" + pio + "]";
    }

    public ElementToSort(int date, int pio) {
        super();
        this.date = date;
        this.pio = pio;
    }

    public int getDate() {
        return date;
    }

    public void setDate(int date) {
        this.date = date;
    }

    public int getPio() {
        return pio;
    }

    public void setPio(int pio) {
        this.pio = pio;
    }

}

Rather than reinventing the wheel, and since you are using Guava, use a TreeMultimap since in this Multimap implementation, "keys and values are ordered by their natural ordering or by supplied comparators."

You create one using static factory methods; for instance, this one .

However : please note that it implements SetMultimap , as such you cannot have duplicate elements in values.

What I would do is to first iterate on the data to retrieve the minimum date for each pio , then just sort using Collections.sort() based on the pio , and if the pio are the same, on the date :

public static List<ElementToSort> sort(final List<ElementToSort> testData) {
    // minimum date associated to each pio
    final Map<Integer, Integer> minDates = new HashMap<Integer, Integer>();

    for (final ElementToSort voy : testData) {
        if (!minDates.containsKey(voy.getPio()) || minDates.get(voy.getPio()) > voy.getDate()) {
            minDates.put(voy.getPio(), voy.getDate());
        }
    }

    // copy testData in case it's read-only
    final List<ElementToSort> sortedData = new ArrayList<ElementToSort>(testData);

    Collections.sort(sortedData, new Comparator<ElementToSort>() {
        @Override
        public int compare(ElementToSort voy1, ElementToSort voy2) {
            int cmp = minDates.get(voy1.getPio()) - minDates.get(voy2.getPio());
            // just in case we have different pio with the same date
            if (cmp == 0) {
                cmp = voy1.getPio() - voy2.getPio();
            }
            if (cmp == 0) {
                cmp = voy1.getDate() - voy2.getDate();
            }
            return cmp;
        }
    });

    return sortedData;
}

Output on the test data:

[[0:3], [1:2], [1:5], [2:5], [2:1], [2:1], [3:1], [4:4]]

EDIT: As suggested by Olivier Grégoire, you can also use Guava's ComparisonChain in the Comparator for more compact code:

    Collections.sort(sortedData, new Comparator<ElementToSort>() {
        @Override
        public int compare(ElementToSort voy1, ElementToSort voy2) {
            return ComparisonChain.start()
                .compare(minDates.get(voy1.getPio()), minDates.get(voy2.getPio()))
                .compare(voy1.getPio(), voy2.getPio())
                .compare(voy1.getDate(), voy2.getDate())
                .result();
        }
    });

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