【问题标题】:Java Comparator Issue (Compare twice)Java比较器问题(比较两次)
【发布时间】:2021-11-18 07:12:32
【问题描述】:

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

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

【问题讨论】:

  • 请显示minimal reproducible example。也就是说,我可以将一些代码复制并粘贴到我的 IDE 中,并能够运行它来重现您的输出。例如你应该展示你是如何创建数组的,以及创建对象的类。
  • 数组从索引 0 开始,因此您不会打印第一个元素。 Sort 将对整个数组进行排序,但无论数组大小如何,您都只能打印到索引 n。

标签: java arrays comparator


【解决方案1】:

如果您只想对数组的一部分进行排序(从索引 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));

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 由于字段是原始 int,使用 comparingIntthenComparingInt 将避免整数的装箱和拆箱。
    • 这也无助于正确排序所有字段为 0 的“空”项目。
    【解决方案3】:

    输入数组似乎包含多个“空”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 提供了一个基于Arrays::sort(T[] arr, int from, int to, Comparator&lt;T&gt; comparator) 方法的interesting solution[1..n+1] 范围内的数组进行排序。

    在这种情况下 item[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
    

    【讨论】:

      【解决方案4】:

      假设您的类有字段的访问器,您可以使用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);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-08
        • 1970-01-01
        相关资源
        最近更新 更多