简体   繁体   中英

How to sort Enums by Counting Sort?

For example, I want to sort Enum. And I have an object with key as Enum

UPDATED

public class Main {

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

        Bean[] mass = new Bean[] { new Bean(new Object(), A), new Bean(new Object(), C), new Bean(new Object(), D),
                new Bean(new Object(), B), new Bean(new Object(), A) }; // 1, 3, 4, 2, 1
        Arrays.sort(mass, new EnumComparator());
        System.out.println(Arrays.toString(mass)); //[1, 1, 2, 3, 4]

    }
}

class EnumComparator implements Comparator<Bean> {

    @Override
    public int compare(Bean o1, Bean o2) {
        return o1.key.toString().compareTo(o2.key.toString());
    }

}

class Bean {
    public Object data;
    public Enum key;

    public Bean(Object data, Enum key) {
        super();
        this.data = data;
        this.key = key;
    }

    @Override
    public String toString() {
        return key.toString();
    }

}

enum MyEnum {

    D("4"),
    A("1"),
    B("2"),
    C("3");

    private String index;

    private MyEnum(String index) {
        this.index = index;
    }

    @Override
    public String toString() {
        return index;
    }

}

Sorting Arrays.sort uses TimSort or MergeSort to run on average O (n log n) . But if we use a finite number of constants ( Enums ), we can use the counting sort in time O (n) . Is there a standard mechanism for using counting sort for Enums in java ?

If you know the order of the constants in advance you can use a HashMap to implement a counting sort:

List<MyEnum> countingSort(MyEnum order[], List<MyEnum> input) {
    HashMap<MyEnum, Integer> countMap = new HashMap<>();
    for (MyEnum obj : input) {
        countMap.put(obj, countMap.getOrDefault(obj, 0) + 1);
    }

    List<MyEnum> result = new ArrayList<>();
    for (MyEnum obj : order) {
        for (int i = 0; i < countMap.getOrDefault(obj, 0); ++i) {
            result.add(obj);
        }
    }

    return result;
}

public static void main (String[] args) {   
    MyEnum order[] = {A, B, C, D};
    List<MyEnum> input = Arrays.asList(D, C, A, B, D, D, B, A, A, C, D);
    List<MyEnum> res = countingSort(order, input);
} 

The complexity of this approach is O(n) in average.


In the updated version of the question, you are asking about pigeonhole sort . It is similar to counting sort but has its own name. We need several changes in the algorithm above. First, we need to replace a HashMap<MyEnum, Integer> with HashMap<MyEnum, List<Bean>> , and store all Bean objects in the corresponding lists. Then after iteration through the input, we need to join all that lists in the specified order.

A distinct non answer here: don't.

Do not worry about O(n) vs O(n*log(n)). Worry about writing human readable code that can be maintained and enhanced over time.

There are two cases:

  • n is so large that the difference matters. This means that n is probably a really large number. Then you are facing a truck load of other, more important aspects you should first focus on. Because then each and any step in your "processing chain" matters. In such cases, you have to carefully look at each step, and determine how to optimize it (and most likely, you have to do measurements prior being able to apply optimizations that make a difference)
  • n is so small that ... uups: n is small. Meaning we are talking about "less than milli seconds" in the first place.

Long story short: this sounds like a typical pre-mature optimisation. And then the only valid answer is: focus on writing clean, elegant code that gets the job done. Then measure with real data. And then decide if further action is required (and most of times: it is not).

Ok, no discussing about should we use O(n) or O(nlogn) is enough. Anyway, probably you could implement custom counting sort algorithm? (I do not remember that any lib I know have this):

private static void countSort(Bean[] mass) {
    List<Bean>[] arr = (List<Bean>[])new List[MyEnum.values().length];

    for (Bean bean : mass) {
        int pos = Integer.parseInt(bean.key.getIndex()) - 1;

        if (arr[pos] == null)
            arr[pos] = new ArrayList<>();

        arr[pos].add(bean);
    }

    int i = 0;

    for (List<Bean> beans : arr)
        for (Bean bean : beans)
            mass[i++] = bean;
}

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