As Anna stated above, counting sort can be a really good algorithm, considering you don't have a really large data set and the data is not sparse.
For example, an array of size 10k with 100 elements duplicated will have much better space efficiency than an array of size 10k with all unique elements and spread in a sparse fashion.
For example, the following array -> [5,5,4,...,2,2,1,1,5,6,7,8] will need a space of an array of size 8 (1 being the minimum and 8 being the maximum) while,
This array -> [5,100,6004,3248,45890,2384,128,8659,...,3892,128] will need a space of an array at least of size 45886 (5 being the minimum and 45890 being the maximum).
So, I'll suggest you use this algorithm when you know that the data set you have is evenly distributed within an acceptable range which won't make your program run out of memory. Otherwise you can go with something like quicksort or mergesort. That gets the work done just fine.
That being said, Anna's implementation of counting sort seemed a little over coded to me personally, so here's me sharing my implementation.
public int[] countSort(int[] nums) {
int min = nums[0], max = nums[0], counterLength, start = 0;
int[] counter;
// To dynamically allocate size to the counter.
// Also an essential step if there are negative elements in the input array.
// You can actively avoid this step if you know:
// 1. That the elements are not going to be negative.
// 2. The upper bound of the elements in the array.
for (int i : nums) {
if (i > max)
max = i;
else if (i < min)
min = i;
}
counterLength = max - min + 1;
counter = new int[counterLength];
for (int i : nums)
counter[i - min]++;
for (int i = 0; i < counterLength; i++) {
if (counter[i] > 0) {
int end = start + counter[i];
Arrays.fill(nums, start, end, i + min);
start = end;
}
}
return nums;
}