Java 比较器问题(比较两次)

Java Comparator Issue (Compare twice)

我有一个名为数组的对象数组,其中有 3 个项目,它们是:idnumAccessnumDelete (全部为原始整数)。 我想做什么: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);
}

假设 before sort 是这样的:

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

那么desired after sort应该是:

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
ID: 6 numAccess: 0 numDelete: 6
ID: 1 numAccess: 0 numDelete: 8
ID: 2 numAccess: 4 numDelete: 15

我不知道出了什么问题。我可以得到一些建议吗?非常感谢。

如果你只想对数组的一部分进行排序(从索引 1 到 n),你可以使用 Arrays.sort(array, 1, n+1, comparator);

另请注意,您的比较器实现过于复杂。你可以替换

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

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

假设您的 class 具有 字段访问器 ,您可以使用 Comparator#comparing methods 来简化比较器逻辑。

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

此外,请注意数组索引从 0 开始,因此您的 for 循环应该像,

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

如果您正在使用 Java 8,您可以使用方法 Comparator::comparing 开始对第一个字段进行排序,然后使用一系列 Comparator::thenComparing 方法来选择后续字段进行排序在

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

似乎输入数组包含几个“空”Process值(ID == 0),因此需要自定义排序以将这些值放在数组末尾,或输入数组需要过滤以排除此类值。

  1. 将带有ID == 0的值放在最后 需要实现自定义进程id排序的方法:
// MyClass
public static int getIdForSorting(Process p) {
    return p.getId() == 0 ? Integer.MAX_VALUE : 0;
}

那么在排序时可能会用到:

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

使用for循环打印数组内容时,索引必须从0开始。

for (int i = 0; i < n; i++) {
    System.out.println(arr[i]); // assuming method `toString` overridden in Process
}
  1. 使用 Stream API 过滤掉“空”进程
Process[] sortedArray = Arrays.stream(array)
      .filter(p -> p.getId() > 0)
      .sorted(Comparator
          .comparingInt(Process::getNumAccess)
          .thenComparingInt(Process::getNumDelete)
      )
      .toArray(Process[]::new);

此处“空”值在排序后被截断,因此可以使用for-each循环:

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

@ThomasKläger 提供了一种 based on Arrays::sort(T[] arr, int from, int to, Comparator<T> comparator) 方法来对范围 [1..n+1].

中的数组进行排序

在这种情况下,项目 [0] 和数组的尾部保持未排序。


测试:

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