I have the following 3 arrays:
int[] indexes = new int[]{0,2,8,5};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};
I want to sort the three arrays based on the indexes:
indexes -> {0,2,5,8}
sources -> {"how", "are", "you", "today"}
targets -> {"I", "am", "fine", "thanks"}
I can create a new class myClass
with all three elements:
class myClass {
int x;
String source;
String target;
}
Reassign everything to myClass, then sort myClass
using x
. However, this would required additional spaces. I am wondering if it is possible to do in place
sorting? Thanks!
Three ways of doing this
1. Using Comparator (Need Java 8 plus)
import java.io.*;
import java.util.*;
class Test {
public static String[] sortWithIndex (String[] strArr, int[] intIndex )
{
if (! isSorted(intIndex)){
final List<String> stringList = Arrays.asList(strArr);
Collections.sort(stringList, Comparator.comparing(s -> intIndex[stringList.indexOf(s)]));
return stringList.toArray(new String[stringList.size()]);
}
else
return strArr;
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i + 1] < arr[i]) {
return false;
};
}
return true;
}
// Driver program to test function.
public static void main(String args[])
{
int[] indexes = new int[]{0,2,8,5};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};
String[] sortedSources = sortWithIndex(sources,indexes);
String[] sortedTargets = sortWithIndex(targets,indexes);
Arrays.sort(indexes);
System.out.println("Sorted Sources " + Arrays.toString(sortedSources) + " Sorted Targets " + Arrays.toString(sortedTargets) + " Sorted Indexes " + Arrays.toString(indexes));
}
}
Output
Sorted Sources [how, are, you, today] Sorted Targets [I, am, fine, thanks] Sorted Indexes [0, 2, 5, 8]
2. Using Lambda (Need Java 8 plus)
import java.io.*;
import java.util.*;
public class Test {
public static String[] sortWithIndex (String[] strArr, int[] intIndex )
{
if (! isSorted(intIndex)) {
final List<String> stringList = Arrays.asList(strArr);
Collections.sort(stringList, (left, right) -> intIndex[stringList.indexOf(left)] - intIndex[stringList.indexOf(right)]);
return stringList.toArray(new String[stringList.size()]);
}
else
return strArr;
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i + 1] < arr[i]) {
return false;
};
}
return true;
}
// Driver program to test function.
public static void main(String args[])
{
int[] indexes = new int[]{0,2,5,8};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};
String[] sortedSources = sortWithIndex(sources,indexes);
String[] sortedTargets = sortWithIndex(targets,indexes);
Arrays.sort(indexes);
System.out.println("Sorted Sources " + Arrays.toString(sortedSources) + " Sorted Targets " + Arrays.toString(sortedTargets) + " Sorted Indexes " + Arrays.toString(indexes));
}
}
3. Using Lists and Maps and avoiding multiple calls (as in second solution above) to the method to sort individual arrays
import java.util.*;
import java.lang.*;
import java.io.*;
public class Test{
public static <T extends Comparable<T>> void sortWithIndex( final List<T> key, List<?>... lists){
// input validation
if(key == null || lists == null)
throw new NullPointerException("Key cannot be null.");
for(List<?> list : lists)
if(list.size() != key.size())
throw new IllegalArgumentException("All lists should be of the same size");
// Lists are size 0 or 1, nothing to sort
if(key.size() < 2)
return;
// Create a List of indices
List<Integer> indices = new ArrayList<Integer>();
for(int i = 0; i < key.size(); i++)
indices.add(i);
// Sort the indices list based on the key
Collections.sort(indices, new Comparator<Integer>(){
@Override public int compare(Integer i, Integer j) {
return key.get(i).compareTo(key.get(j));
}
});
Map<Integer, Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
List<Integer> swapFrom = new ArrayList<Integer>(indices.size()),
swapTo = new ArrayList<Integer>(indices.size());
// create a mapping that allows sorting of the List by N swaps.
for(int i = 0; i < key.size(); i++){
int k = indices.get(i);
while(i != k && swapMap.containsKey(k))
k = swapMap.get(k);
swapFrom.add(i);
swapTo.add(k);
swapMap.put(i, k);
}
// use the swap order to sort each list by swapping elements
for(List<?> list : lists)
for(int i = 0; i < list.size(); i++)
Collections.swap(list, swapFrom.get(i), swapTo.get(i));
}
public static void main (String[] args) throws java.lang.Exception{
List<Integer> index = Arrays.asList(0,2,8,5);
List<String> sources = Arrays.asList("how", "are", "today", "you");
// List Types do not need to be the same
List<String> targets = Arrays.asList("I", "am", "thanks", "fine");
sortWithIndex(index, index, sources, targets);
System.out.println("Sorted Sources " + sources + " Sorted Targets " + targets + " Sorted Indexes " + index);
}
}
Output
Sorted Sources [how, are, you, today] Sorted Targets [I, am, fine, thanks] Sorted Indexes [0, 2, 5, 8]
It is possible although it is not that easy than it looks like. There are two options:
write your own sort algorithm where the swap function for two elements also swaps the elements in the other arrays.
AFAIK there is no way to extend the standard
Array.sort
in a way that it swaps additional arrays.Use a helper array with the sort order.
First of all you need to initialize the helper array with the range
{0, 1 ... indexes.Length-1}
.Now you sort the helper array using a
Comparator
that comparesindexes[a]
withindexes[b]
rather thana
tob
. The result is an helper array where each element has the index of the element of the source array where its content should come from, i.e. the sort sequence.The last step is the most tricky one. You need to swap the elements in your source arrays according to the sort sequence above.
To operate strictly in place set your current indexcur
to0
.
Then take thecur
-th element from your helper array. Let's call itfrom
. This is the element index that should be placed at indexcur
after completion.
Now you need to make space at indexcur
to place the elements from indexfrom
there. Copy them to a temporary locationtmp
.
Now move the elements from indexfrom
to indexcur
. Indexfrom
is now free to be overridden.
Set the element in the helper array at indexcur
to some invalid value, e.g.-1
.
Set your current indexcur
tofrom
proceed from above until you reach an element in the helper array which already has an invalid index value, i.e. your starting point. In this case store the content oftmp
at the last index. You now have found a closed loop of rotated indices.
Unfortunately there may exist an arbitrary number of such loops each of arbitrary size. So you need to seek in the helper array for the next non-invalid index value and again continue from above until all elements of the helper array are processed. Since you will end at the starting point after each loop it is sufficient to incrementcur
unless you find an non-invalid entry. So the algorithm is still O(n) while processing the helper array. All entries beforecur
are necessarily invalid after a loop completed.
Ifcur
increments beyond the size of the helper array you are done.
There is an easier variation of option 2 when you are allowed to create new target arrays.
In this case you simply allocate the new target arrays and fill their content according to the indices in your helper array.
The drawback is that the allocations might be quite expensive if the arrays are really large. And of course, it is no longer in place.
Some further notes.
Normally the custom sort algorithm performs better as it avoids the allocation of the temporary array. But in some cases the situation changes. The processing of the cyclic element rotation loops uses a minimum move operations. This is O(n) rather than O(n log n) of common sort algorithms. So when the number of arrays to sort and or the size of the arrays grows the method #2 has an advantage because it uses less swap operations.
A data model requiring a sort algorithm like this is mostly broken by design. Of course, like always there are a few cases where you can't avoid this.
May I suggest you to use a TreeMap
or something similar, using your integer as key.
static Map<Integer, myClass> map = new TreeMap<>();
So when you want to retrieve ordered you only have to do a for loop or whatever you prefer.
for (int i : map.keyset()){
System.out.println("x: "+map.get(i).x+"\nsource: "+map.get(i).source+"\ntarget: "+map.get(i).target);
}
This example requires creating an Integer array of indexes, but the arrays to be sorted are reordered in place according to array1, and the arrays can be of any type (primitives or objects) that allows indexing.
public static void main(String[] args) {
int array1[]={5,1,9,3,8};
int array2[]={2,0,3,6,1};
int array3[]={3,1,4,5,9};
// generate array of indices
Integer[] I = new Integer [array1.length];
for(int i = 0; i < I.length; i++)
I[i] = i;
// sort array of indices according to array1
Arrays.sort(I, (i, j) -> array1[i]-array1[j]);
// reorder array1 ... array3 in place using sorted indices
// also reorder indices back to 0 to length-1
// time complexity is O(n)
for(int i = 0; i < I.length; i++){
if(i != I[i]){
int t1 = array1[i];
int t2 = array2[i];
int t3 = array3[i];
int j;
int k = i;
while(i != (j = I[k])){
array1[k] = array1[j];
array2[k] = array2[j];
array3[k] = array3[j];
I[k] = k;
k = j;
}
array1[k] = t1;
array2[k] = t2;
array3[k] = t3;
I[k] = k;
}
}
// display result
for (int i = 0; i < array1.length; i++) {
System.out.println("array1 " + array1[i] +
" array2 " + array2[i] +
" array3 " + array3[i]);
}
}
Another solution using Collection
(increase the memory usage) :
Let's create a sorted map to will simply be a mapping between the correct index and the original position :
public static TreeMap<Integer, Integer> sortIndex(int[] array){
TreeMap<Integer, Integer> tree = new TreeMap<>();
for(int i=0; i < array.length; ++i) {
tree.put(array[i], i);
}
return tree;
}
Test :
int[] indexes = new int[] { 0, 1, 3, 2, 4, 5 };
TreeMap<Integer, Integer> map = sortIndex(indexes);
map.keySet().stream().forEach(System.out::print); //012345
map.values().stream().forEach(System.out::print); //013245
We have the indexes sorted (on the key) and the original index order as the values.
No we can simple use this to order the array, I will be drastic and use a Stream
to map and collect into a List
.
public static List<String> sortInPlace(String[] array, TreeMap<Integer, Integer> map) {
return map.values().stream().map(i -> array[i]).collect(Collectors.toList());
}
Test :
String[] sources = "to be not or to be".split(" ");
int[] indexes = new int[] { 0, 1, 3, 2, 4, 5 };
TreeMap<Integer, Integer> map = sortIndex(indexes);
List<String> result = sortInPlace(sources, map);
System.out.println(result);
[to, be, or, not, to, be]
Why did I use a List
. Mostly to simplify the re-ordering, if we try to order the original arrays, it will be complicated because we need to remove the opposed key/pair
2 -> 3
3 -> 2
Without some cleaning, we will just swap the cells twice ... so there will be no changes.
If we want to reduce a bit the memory usage, we can create another array instead of using the stream and copy values per values iterating the map. This would be possible to do with multiple array in parallel too.
It all depends on the size of your arrays. This solution will use the first array to perform the sorting but will perform the permutation on multiple arrays.
So this could have some performances issues if the sorting algorithm used will need a lot of permutation.
Here, I took a basic sorting algorithm on which I have added some actions I can do during the swap of two cells. This allows use to define some lambda to swap multiple array at the same time based on one array.
public static void sortArray( int[] array, BiConsumer<Integer, Integer>... actions ) {
int tmp;
for ( int i = 0, length = array.length; i < length; ++i ) {
tmp = array[i];
for ( int j = i + 1; j < length; ++j ) {
if ( tmp > array[j] ) {
array[i] = array[j];
array[j] = tmp;
tmp = array[i];
// Swap the other arrays
for ( BiConsumer<Integer, Integer> cons : actions ){
cons.accept( i, j);
}
}
}
}
}
Let's create a generic method to swap the cells that we can pass as a BiConsumer
lambda (only works for non-primitive arrays):
public static <T> void swapCell( T[] array, int from, int to ) {
T tmp = array[from];
array[from] = array[to];
array[to] = tmp;
}
That allows use to sort the arrays like :
public static void main( String[] args ) throws ParseException {
int[] indexes = new int[] { 0, 2, 8, 5 };
String[] sources = new String[] { "how", "are", "today", "you" };
String[] targets = new String[] { "I", "am", "thanks", "fine" };
sortArray( indexes,
( i, j ) -> swapCell( sources, i, j ),
( i, j ) -> swapCell( targets, i, j ) );
System.out.println( Arrays.toString( indexes ) );
System.out.println( Arrays.toString( sources ) );
System.out.println( Arrays.toString( targets ) );
}
[0, 2, 5, 8]
[how, are, you, today]
[I, am, fine, thanks]
This solution does not required (much) more memory than the one already used since no additional array or Collection
are required.
The use of BiConsumer<>...
provide a generic solution, this could also accept an Object[]...
but this would not work for primitives array anymore. This have a slight performance lost of course, so based on the need, this can be removed.
Creation of a complete solution, first let's define an interface that will be used as a factory as well :
interface Sorter {
void sort(int[] array, BiConsumer<Integer, Integer>... actions);
static void sortArrays(int[] array, BiConsumer<Integer, Integer>... actions){
// call the implemented Sorter
}
}
Then, implement a simple Selection sorterr with the same logic as before, for each permutation in the original array, we execute the BiConsumer
:
class SelectionSorter implements Sorter {
public void sort(int[] array, BiConsumer<Integer, Integer>... actions) {
int index;
int value;
int tmp;
for (int i = 0, length = array.length; i < length; ++i) {
index = i;
value = array[i];
for (int j = i + 1; j < length; ++j) {
if (value > array[j]) {
index = j;
value = array[j];
}
}
if (index != i) {
tmp = array[i];
array[i] = array[index];
array[index] = tmp;
// Swap the other arrays
for (BiConsumer<Integer, Integer> cons : actions) {
cons.accept(i, index);
}
}
}
}
}
Let also create a Bubble sorter :
class BubbleSorter implements Sorter {
public void sort(int[] array, BiConsumer<Integer, Integer>... actions) {
int tmp;
boolean swapped;
do {
swapped = false;
for (int i = 1, length = array.length; i < length; ++i) {
if (array[i - 1] > array[i]) {
tmp = array[i];
array[i] = array[i - 1];
array[i - 1] = tmp;
// Swap the other arrays
for (BiConsumer<Integer, Integer> cons : actions) {
cons.accept(i, i - 1);
}
swapped = true;
}
}
} while (swapped);
}
}
Now, we can simple call one or the other based on a simple condition, the length :
static void sortArrays(int[] array, BiConsumer<Integer, Integer>... actions){
if(array.length < 1000){
new BubbleSorter().sort(array, actions);
} else {
new SelectionSorter().sort(array, actions);
}
}
That way, we can call our sorter simply with
Sorter.sortArrays(indexes,
(i, j) -> swapCell(sources, i, j),
(i, j) -> swapCell(targets, i, j)
);
Complete test case on ideone (limit on size because of the time out)
I wonder if my approach is valid.
public class rakesh{
public static void sort_myClass(myClass myClasses[]){
for(int i=0; i<myClasses.length; i++){
for(int j=0; j<myClasses.length-i-1; j++){
if(myClasses[j].x >myClasses[j+1].x){
myClass temp_myClass = new myClass(myClasses[j+1]);
myClasses[j+1] = new myClass(myClasses[j]);
myClasses[j] = new myClass(temp_myClass);
}
}
}
}
public static class myClass{
int x;
String source;
String target;
myClass(int x,String source,String target){
this.x = x;
this.source = source;
this.target = target;
}
myClass(myClass super_myClass){
this.x = super_myClass.x;
this.source = super_myClass.source;
this.target = super_myClass.target;
}
}
public static void main(String args[]) {
myClass myClass1 = new myClass(0,"how","I");
myClass myClass2 = new myClass(2,"are","am");
myClass myClass3 = new myClass(8,"today","thanks");
myClass myClass4 = new myClass(5,"you","fine");
myClass[] myClasses = {myClass1, myClass2, myClass3, myClass4};
sort_myClass(myClasses);
for(myClass myClass_dummy : myClasses){
System.out.print(myClass_dummy.x + " ");
}
System.out.print("\n");
for(myClass myClass_dummy : myClasses){
System.out.print(myClass_dummy.source + " ");
}
System.out.print("\n");
for(myClass myClass_dummy : myClasses){
System.out.print(myClass_dummy.target + " ");
}
}
}
If you find any error or have suggestions then please leave a comment so I could make any necessary edits.
Output
0 2 5 8
how are you today
I am fine thanks
Process finished with exit code 0
without assign values in class, you can achieve it with following code:
Integer[] indexes = new Integer[]{0,2,8,5};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};
Integer[] sortedArrya = Arrays.copyOf(indexes, indexes.length);
Arrays.sort(sortedArrya);
String[] sortedSourses = new String[sources.length];
String[] sortedTargets = new String[targets.length];
for (int i = 0; i < sortedArrya.length; i++) {
int intValus = sortedArrya[i];
int inx = Arrays.asList(indexes).indexOf(intValus);
sortedSourses[i] = sources[+inx];
sortedTargets[i] = targets[+inx];
}
System.out.println(sortedArrya);
System.out.println(sortedSourses);
System.out.println(sortedTargets);
I have an other solution for your question:
private void reOrder(int[] indexes, String[] sources, String[] targets){
int[] reIndexs = new int[indexes.length]; // contain index of item from MIN to MAX
String[] reSources = new String[indexes.length]; // array sources after re-order follow reIndexs
String[] reTargets = new String[indexes.length]; // array targets after re-order follow reIndexs
for (int i=0; i < (indexes.length - 1); i++){
if (i == (indexes.length - 2)){
if (indexes[i] > indexes[i+1]){
reIndexs[i] = i+1;
reIndexs[i+1] = i;
}else
{
reIndexs[i] = i;
reIndexs[i+1] = i+1;
}
}else
{
for (int j=(i+1); j < indexes.length; j++){
if (indexes[i] > indexes[j]){
reIndexs[i] = j;
}else {
reIndexs[i] = i;
}
}
}
}
// Re-order sources array and targets array
for (int index = 0; index < reIndexs.length; index++){
reSources[index] = sources[reIndexs[index]];
reTargets[index] = targets[reIndexs[index]];
}
// Print to view result
System.out.println( Arrays.toString(reIndexs));
System.out.println( Arrays.toString(reSources));
System.out.println( Arrays.toString(reTargets));
}
You can also achieve in your way too.
Here I created an ArrayList myArr
and sorted Based on index value and then converted back to the array if you satisfied with ArrayList just you can remove the conversion or you want Array this one be helpful.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
public class StackOverflow {
public static void main(String[] args) {
int[] indexes = new int[]{0,2,8,5};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};
ArrayList<myClass> myArr=new ArrayList<>();
for(int i=0;i<indexes.length;i++) {
myArr.add(new myClass(indexes[i], sources[i], targets[i]));
}
//Collections.sort(myArr,new compareIndex());
// Just for readability of code
Collections.sort(myArr, (mC1, mC2) -> mC1.getX() - mC2.getX());
//Conversion Part
for (int i=0;i<myArr.size();i++){
indexes[i]=myArr.get(i).getX();
sources[i]=myArr.get(i).getSource();
targets[i]=myArr.get(i).getTarget();
}
System.out.println(Arrays.toString(indexes));
System.out.println(Arrays.toString(sources));
System.out.println(Arrays.toString(targets));
}
}
class myClass {
private Integer x;
private String source;
private String target;
public myClass(Integer x,String source,String target){
this.x=x;
this.source=source;
this.target=target;
}
public Integer getX() {
return x;
}
public String getSource() {
return source;
}
public String getTarget() {
return target;
}
}
来源:https://stackoverflow.com/questions/50336770/sort-multiple-arrays-simultaneously-in-place