Home > Mobile >  Java Comparator Issue (Compare twice)
Java Comparator Issue (Compare twice)

Time:09-26

I have an array of objects called array and have 3 items in it, which are: id, numAccess and numDelete (all in primitive int). What I want to do: sort first by numAccess then by numDelete.

for(int i=1; i<=n; i  ){
     System.out.println("ID: "   array[i].id   " numAccess: "   array[i].numAccess   " numDelete: "   array[i].numDelete);
}

Arrays.sort(array, new Comparator<Process>(){
     public int compare(Process p1, Process p2) {
          Integer compr = Integer.valueOf(p1.numAccess).compareTo(Integer.valueOf(p2.numAccess));
        
          if (compr != 0) {
               return compr;
          } 
        
          return Integer.valueOf(p1.numDelete).compareTo(Integer.valueOf(p2.numDelete)); 
     }
});

for(int i=1; i<=n; i  ){
      System.out.println("ID: "   array[i].id   " numAccess: "   array[i].numAccess   " numDelete: "   array[i].numDelete);
}

Let's say if before sort is this:

ID: 1 numAccess: 0 numDelete: 8
ID: 2 numAccess: 4 numDelete: 15
ID: 3 numAccess: 7 numDelete: 9
ID: 4 numAccess: 13 numDelete: 5
ID: 5 numAccess: 9 numDelete: 13
ID: 6 numAccess: 0 numDelete: 6

Then desired after sort should be:

ID: 6 numAccess: 0 numDelete: 6
ID: 1 numAccess: 0 numDelete: 8
ID: 2 numAccess: 4 numDelete: 15
ID: 3 numAccess: 7 numDelete: 9
ID: 5 numAccess: 9 numDelete: 13
ID: 4 numAccess: 13 numDelete: 5

However, what I get is this:

ID: 0 numAccess: 0 numDelete: 0
ID: 0 numAccess: 0 numDelete: 0
ID: 0 numAccess: 0 numDelete: 0
ID: 6 numAccess: 0 numDelete: 6
ID: 1 numAccess: 0 numDelete: 8
ID: 2 numAccess: 4 numDelete: 15

I couldn't figure out what's wrong. May I get some advice? Thank you very much.

CodePudding user response:

It seems the input array contains several "empty" Process values (with ID == 0), so sorting needs to be customized to put such values to the end of array, or the input array needs to be filtered to exclude such values.

  1. Put to the end the values with ID == 0 A method to customize process id for sorting needs to be implemented:
// MyClass
public static int getIdForSorting(Process p) {
    return p.getId() == 0 ? Integer.MAX_VALUE : 0;
}

Then it may be used in sorting:

Arrays.sort(array, Comparator
    .comparingInt(MyClass::getIdForSorting)
    .thenComparingInt(Process::getNumAccess)
    .thenComparingInt(Process::getNumDelete)
);

When printing the array contents using for loop, indexing has to start from 0.

for (int i = 0; i < n; i  ) {
    System.out.println(arr[i]); // assuming method `toString` overridden in Process
}
  1. Use Stream API to filter out "empty" processes
Process[] sortedArray = Arrays.stream(array)
      .filter(p -> p.getId() > 0)
      .sorted(Comparator
          .comparingInt(Process::getNumAccess)
          .thenComparingInt(Process::getNumDelete)
      )
      .toArray(Process[]::new);

Here "empty" values are truncated after sorting, so for-each loop may be used:

for (Process process : sortedArray) {
    System.out.println(process); // assuming method `toString` overridden in Process
}

@ThomasKläger offered an interesting solution based on Arrays::sort(T[] arr, int from, int to, Comparator<T> comparator) method to sort the array in range [1..n 1].

In this case item[0] and the tail of the array remains unsorted.


Tests:

Process[] array = {
    new Process(), new Process(1,  0,  8), new Process(2,  4, 15),
    new Process(3,  7,  9), new Process(4, 13,  5), new Process(5,  9, 13),
    new Process(6,  0,  6), new Process(), new Process()
};
// after sorting with getIdForSorting
ID: 6   numAccess: 0    numDelete: 6
ID: 1   numAccess: 0    numDelete: 8
ID: 2   numAccess: 4    numDelete: 15
ID: 3   numAccess: 7    numDelete: 9
ID: 5   numAccess: 9    numDelete: 13
ID: 4   numAccess: 13   numDelete: 5
ID: 0   numAccess: 0    numDelete: 0
ID: 0   numAccess: 0    numDelete: 0
ID: 0   numAccess: 0    numDelete: 0

// after filtering/truncating
ID: 6   numAccess: 0    numDelete: 6
ID: 1   numAccess: 0    numDelete: 8
ID: 2   numAccess: 4    numDelete: 15
ID: 3   numAccess: 7    numDelete: 9
ID: 5   numAccess: 9    numDelete: 13
ID: 4   numAccess: 13   numDelete: 5

// after sorting the subrange
ID: 0   numAccess: 0    numDelete: 0
ID: 6   numAccess: 0    numDelete: 6
ID: 1   numAccess: 0    numDelete: 8
ID: 2   numAccess: 4    numDelete: 15
ID: 3   numAccess: 7    numDelete: 9
ID: 5   numAccess: 9    numDelete: 13
ID: 4   numAccess: 13   numDelete: 5
ID: 0   numAccess: 0    numDelete: 0
ID: 0   numAccess: 0    numDelete: 0

CodePudding user response:

If you want to only sort part of the array (from index 1 to n inclusive) you can use Arrays.sort(array, 1, n 1, comparator);

Also note that your comparator implementation is unnecessarily complex. You can replace

Integer compr = Integer.valueOf(p1.numAccess).compareTo(Integer.valueOf(p2.numAccess));

with

int compr = Integer.compare(p1.numAccess, p2.numAccess));

CodePudding user response:

If you're using Java 8, you can use method Comparator::comparing to start sorting on first field and then a chain of Comparator::thenComparing methods to choose the subsequent fields to sort on

Comparator<Process> processComparator = Comparator.comparing(Process::getNumAccess)
.thenComparing(Process::numDelete);
        Arrays.sort(array, processComparator);

CodePudding user response:

Assuming your class has accessors for the fields, you can use Comparator#comparing methods to simplify your comparator logic.

Arrays.sort(array, Comparator.comparingInt(Array::getNumAccess)
            .thenComparingInt(Array::getNumDelete));

Also, note that array indices start at 0 and hence your for loop should be like,

for (int i = 0; i < n; i  ) {
    System.out.println("ID: "   array[i].id   " numAccess: "   array[i].numAccess   " numDelete: "   array[i].numDelete);
}
  • Related