First, having two parallel arrays with elements in corresponding indexes indicates you might want to change the data structure to a single array with a container object containing two fields. This container object would then implement Comparable interface.
But, sticking with what you say, one approach could be:
/**
* Sorts parallel arrays in-place. Sorted by the first array and updating
* all other arrays to match.
* Uses the natural sorting of the objects.
* All arrays must be the same length.
*
* @param keys the values used to sort, may be duplicate
*
* @param otherArrays the arrays to have reordered to match the sorting of
* the keys array.
*
* @exception IllegalArgumentException if any of otherArrays have a length
* different that the keys array.
*/
public static <E extends Comparable<? super E>> void sortParallelArrays(
E[] keys,
Object[] ... otherArrays
) {
int numKeys = keys.length;
int numOtherArrays = otherArrays.length;
for(Object[] otherArray : otherArrays) {
if(otherArray.length != numKeys) {
throw new IllegalArgumentException("Mismatched array lengths");
}
}
// A list of all indexes per key
// This also does the sorting within the TreeMap using natural ordering
SortedMap<E, List<Integer>> originalIndexesByKey = new TreeMap<E, List<Integer>>();
// Populate the map
for(int i = 0; i < numKeys; i++) {
E key = keys[i];
List<Integer> originalIndexes = originalIndexesByKey.get(key);
if(originalIndexes == null) {
// Optimization for the non-duplicate keys
originalIndexesByKey.put(key, Collections.singletonList(i));
} else {
if(originalIndexes.size() == 1) {
// Upgrade to ArrayList now that know have duplicate keys
originalIndexes = new ArrayList<Integer>(originalIndexes);
originalIndexesByKey.put(key, originalIndexes);
}
originalIndexes.add(i);
}
}
// Store back to keys and sort other arrays in a single traversal
Object[][] sortedOtherArrays = new Object[numOtherArrays][numKeys];
int pos = 0;
for(Map.Entry<E, List<Integer>> entry : originalIndexesByKey.entrySet()) {
E key = entry.getKey();
for(int index : entry.getValue()) {
keys[pos] = key;
for(int ooIndex = 0; ooIndex < numOtherArrays; ooIndex++) {
sortedOtherArrays[ooIndex][pos] = otherArrays[ooIndex][index];
}
pos++;
}
}
assert pos == numKeys : "Arrays should be full";
// Copy back to original arrays for in-place sort
for(int ooIndex = 0; ooIndex < numOtherArrays; ooIndex++) {
System.arraycopy(
sortedOtherArrays[ooIndex], 0,
otherArrays[ooIndex], 0,
numKeys);
}
}
This is not the most memory efficient strategy, but isn't much code.
The time complexity isn't too bad. Looks something like O((M+1)*N*log(N))
, where M
is the number of otherArrays and N
is the number of keys. No crazy worst-case issues, at least.