【问题标题】:Sort arrays of primitive types in descending order按降序对原始类型数组进行排序
【发布时间】:2010-09-17 22:24:50
【问题描述】:

我有一个 原始类型(双精度)数组。 如何按降序对元素进行排序?

不幸的是,Java API 不支持使用 Comparator 对原始类型进行排序。

可能想到的第一种方法是将其转换为对象列表(装箱):

double[] array = new double[1048576];    
Arrays.stream(array).boxed().sorted(Collections.reverseOrder())…

但是,对数组中的每个图元进行装箱太慢并且会导致很大的 GC 压力

另一种方法是排序然后反转:

double[] array = new double[1048576];
...
Arrays.sort(array);
// reverse the array
for (int i = 0; i < array.length / 2; i++) {
     // swap the elements
     double temp = array[i];
     array[i] = array[array.length - (i + 1)];
     array[array.length - (i + 1)] = temp;
}

这种方法也很慢 - 特别是如果数组已经很好地排序了。

有什么更好的选择?

【问题讨论】:

    标签: java sorting


    【解决方案1】:

    这是一个基于内部字段对对象进行排序的完整程序。

    package Algorithms.BranchAndBound;
    
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class KnapSack01 {
        private class ItemVals {
            double weight;
            double cost;
            double ratio;
    
            public ItemVals(double weight, double cost) {
                this.weight = weight;
                this.cost = cost;
                this.ratio = weight/cost;
            }
        }
    
        public ItemVals[] createSortedItemVals(double[] weight, double[] cost) {
            ItemVals[] itemVals = new ItemVals[weight.length];
            for(int i = 0; i < weight.length; i++) {
                ItemVals itemval = new ItemVals(weight[i], cost[i]);
                itemVals[i] = itemval;
            }
            Arrays.sort(itemVals, new Comparator<ItemVals>() {
                @Override
                public int compare(ItemVals o1, ItemVals o2) {
                    return Double.compare(o2.ratio, o1.ratio);
                }
            });
            return itemVals;
        }
    
        public void printItemVals(ItemVals[] itemVals) {
            for (int i = 0; i < itemVals.length; i++) {
                System.out.println(itemVals[i].ratio);
            }
        }
    
        public static void main(String[] args) {
            KnapSack01 knapSack01 = new KnapSack01();
            double[] weight = {2, 3.14, 1.98, 5, 3};
            double[] cost = {40, 50, 100, 95, 30};
            ItemVals[] itemVals = knapSack01.createSortedItemVals(weight, cost);
            knapSack01.printItemVals(itemVals);
        }
    }
    

    【讨论】:

      【解决方案2】:

      这是一个单线,在 Java 8 中使用流

      int arr = new int[]{1,2,3,4,5};
      Arrays.stream(arr).boxed().sorted(Collections.reverseOrder()).mapToInt(Integer::intValue).toArray();
      

      【讨论】:

      • 装箱数组中的每个基元对于大型数组来说太昂贵了!
      • 太好了,谢谢。使用标准的 java 方法有一个工作单行是很好的。如果对于大型数组来说确实太慢了,那么总是可以重构它。
      【解决方案3】:
      double s =-1;
         double[] n = {111.5, 111.2, 110.5, 101.3, 101.9, 102.1, 115.2, 112.1};
         for(int i = n.length-1;i>=0;--i){
            int k = i-1;
            while(k >= 0){
                if(n[i]>n[k]){
                    s = n[k];
                    n[k] = n[i];
                    n[i] = s;
                }
                k --;
            }
         }
         System.out.println(Arrays.toString(n));
       it gives time complexity O(n^2) but i hope its work
      

      【讨论】:

        【解决方案4】:

        了解这是一篇非常古老的帖子,但我在尝试对原始 int 数组进行排序时偶然发现了一个类似的问题,因此发布了我的解决方案。欢迎提出建议/cmets -

        int[] arr = {3,2,1,3};
        List<Integer> list = new ArrayList<>();
        Arrays.stream(arr).forEach(i -> list.add(i));
        list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
        

        【讨论】:

          【解决方案5】:

          在 Java 8 中,更好、更简洁的方法可能是:

          double[] arr = {13.6, 7.2, 6.02, 45.8, 21.09, 9.12, 2.53, 100.4};
          
          Double[] boxedarr = Arrays.stream( arr ).boxed().toArray( Double[]::new );
          Arrays.sort(boxedarr, Collections.reverseOrder());
          System.out.println(Arrays.toString(boxedarr));
          

          这将给出反转的数组,并且更美观。

          输入:[13.6, 7.2, 6.02, 45.8, 21.09, 9.12, 2.53, 100.4]

          输出:[100.4, 45.8, 21.09, 13.6, 9.12, 7.2, 6.02, 2.53]

          【讨论】:

          • 如果投反对票的人能够在此解决方案中解释他的问题,或者在空白投反对票之前纠正我以防万一有更好的答案,那就更好了。
          【解决方案6】:

          以下是我的解决方案,您可以根据自己的需要进行调整。

          它是如何工作的?它接受一个整数数组作为参数。之后,它将创建一个新数组,该数组将包含与参数中的数组相同的值。这样做的原因是保持原数组不变。

          一旦新数组包含复制的数据,我们通过交换值对其进行排序,直到条件 if(newArr[i] 评估为假。这意味着数组按降序排序。

          如需详细解释,请查看我的博文here

          public static int[] sortDescending(int[] array)
          {
              int[] newArr = new int[array.length];
          
              for(int i = 0; i < array.length; i++)
              {
                  newArr[i] = array[i];
              }
          
              boolean flag = true;
              int tempValue;
          
              while(flag) 
              {
                  flag = false;
          
                  for(int i = 0; i < newArr.length - 1; i++) 
                  {
                      if(newArr[i] < newArr[i+1])
                      {
                          tempValue = newArr[i];
                          newArr[i] = newArr[i+1];
                          newArr[i+1] = tempValue;
                          flag = true;
                      }
                  }
              }
          
              return newArr;
          }
          

          【讨论】:

          • 编写自己的排序不是一个好主意 - 它比库中的慢得多
          【解决方案7】:

          如果使用java8,只需将数组转换为流,排序并转换回来。 所有的任务都可以一行完成,所以我觉得这种方式还不错。

          double[] nums = Arrays.stream(nums).boxed().
                  .sorted((i1, i2) -> Double.compare(i2, i1))
                  .mapToDouble(Double::doubleValue)
                  .toArray();
          

          【讨论】:

          • 拳击太贵了。
          【解决方案8】:

          我认为最好不要重新发明*并使用 Arrays.sort()。

          是的,我看到了“下降”部分。排序是困难的部分,您希望从 Java 库代码的简单性和速度中受益。完成后,您只需反转数组,这是一个相对便宜的 O(n) 操作。 Here's some code 我发现只需 4 行就可以做到这一点:

          for (int left=0, right=b.length-1; left<right; left++, right--) {
              // exchange the first and last
              int temp = b[left]; b[left]  = b[right]; b[right] = temp;
          }
          

          【讨论】:

          • 这个答案应该被接受。它比替代方案(装箱 + 排序 + 拆箱,有或没有流)快得多
          【解决方案9】:
          Before sorting the given array multiply each element by -1 
          

          然后使用 Arrays.sort(arr) 然后再次将每个元素乘以 -1

          for(int i=0;i<arr.length;i++)
              arr[i]=-arr[i];
          Arrays.sort(arr);
          for(int i=0;i<arr.length;i++)
              arr[i]=-arr[i];
          

          【讨论】:

          • 遗憾地不适用于Integer.MIN_VALUEideone.com/HBcrI8
          • int[] sorted = IntStream.of(arr).map(i -&gt; ~i).sorted().map(i -&gt; ~i).toArray(); 工作
          【解决方案10】:

          你的算法是正确的。但是我们可以做如下优化: 反转时,您可以尝试保留另一个变量以减少反向计数器,因为计算 array.length-(i+1) 可能需要时间! 并且还将 temp 的声明移到外面,这样每次都不需要分配它

          double temp;
          
          for(int i=0,j=array.length-1; i < (array.length/2); i++, j--) {
          
               // swap the elements
               temp = array[i];
               array[i] = array[j];
               array[j] = temp;
          }
          

          【讨论】:

          • 将数组长度(和数组长度的一半)存储在循环外的变量中是值得的。在循环外声明“temp”没有任何区别——不涉及“分配”。
          【解决方案11】:

          对于小型数组,这可能有效。

          int getOrder (double num, double[] array){
              double[] b = new double[array.length];
              for (int i = 0; i < array.length; i++){
                  b[i] = array[i];
              }
              Arrays.sort(b);
              for (int i = 0; i < b.length; i++){
                  if ( num < b[i]) return i;
              }
              return b.length;
          }
          

          我很惊讶数组 b 的初始加载是必要的

          double[] b = array; // makes b point to array. so beware!
          

          【讨论】:

            【解决方案12】:

            我认为最简单的解决方案仍然是:

            1. 获取数组的自然顺序
            2. 在排序后的数组中找到最大值,然后是最后一项
            3. 使用带有减量运算符的 for 循环

            正如其他人之前所说:使用 toList 是额外的努力,Arrays.sort(array,Collections.reverseOrder()) 不适用于原语,并且当您需要的所有内容已经内置时,使用额外的框架似乎太复杂了,因此可能也更快...

            示例代码:

            import java.util.Arrays;
            
            public class SimpleDescending {
            
                public static void main(String[] args) {
            
                    // unsorted array
                    int[] integerList = {55, 44, 33, 88, 99};
            
                    // Getting the natural (ascending) order of the array
                    Arrays.sort(integerList);
            
                    // Getting the last item of the now sorted array (which represents the maximum, in other words: highest number)
                    int max = integerList.length-1;
            
                    // reversing the order with a simple for-loop
                    System.out.println("Array in descending order:");
                    for(int i=max; i>=0; i--) {
                        System.out.println(integerList[i]);
                    }
            
                    // You could make the code even shorter skipping the variable max and use
                    // "int i=integerList.length-1" instead of int "i=max" in the parentheses of the for-loop
                }
            }
            

            【讨论】:

              【解决方案13】:

              Java Primitive 包含基于自定义比较器对原始数组进行排序的功能。使用它和 Java 8,您的示例可以写成:

              double[] array = new double[1048576];
              ...
              Primitive.sort(array, (d1, d2) -> Double.compare(d2, d1), false);
              

              如果您使用的是 Maven,则可以将其包含在:

              <dependency>
                  <groupId>net.mintern</groupId>
                  <artifactId>primitive</artifactId>
                  <version>1.2.1</version>
              </dependency>
              

              当您将false 作为第三个参数传递给sort 时,它使用了一种不稳定的排序方式,即对Java 内置dual-pivot quicksort 的简单编辑。这意味着速度应该接近内置排序的速度。

              完全披露:我编写了 Java Primitive 库。

              【讨论】:

              • 这是 GitHub 上一个很棒的简单项目。非常好的工作!读者还应注意true 作为sort 的第三个参数将使用Java 内置的TimSort 的简单编辑,它是稳定的。
              【解决方案14】:

              对于数字类型,在排序前后否定元素似乎是一种选择。排序后相对于单个反向的速度取决于缓存,如果反向不是更快,任何差异都可能会在噪音中丢失。

              【讨论】:

              • 添加评论而不是发表评论作为答案。
              • 现在我有足够的声誉可以发表评论,我仍然认为这是一个答案:它描述了How [to] sort the elements in descending order,而sort and then reverse […] is slow,我认为这是衡量速度的一种替代方法。是什么让它成为您眼中的评论而不是答案?
              【解决方案15】:
              double[] array = new double[1048576];
              

              ...

              默认顺序是升序

              颠倒顺序

              Arrays.sort(array,Collections.reverseOrder());
              

              【讨论】:

              • Arrays.sort(array, Collections.reverseOrder()); 是一个很好的解决方案,但它适用于原语。它只适用于class
              • 不是原始类型的解决方案
              • 这不适用于原始类型,这适用于对象类型。
              【解决方案16】:
              Double[] d = {5.5, 1.3, 8.8};
              Arrays.sort(d, Collections.reverseOrder());
              System.out.println(Arrays.toString(d));
              

              Collections.reverseOrder() 不适用于基元,但 Double、Integer 等适用于 Collections.reverseOrder()

              【讨论】:

                【解决方案17】:

                Guava 具有将原始数组转换为包装器类型列表的方法。好的部分是这些列表是实时视图,因此对它们的操作也适用于底层数组(类似于Arrays.asList(),但适用于原语)。

                无论如何,这些列表中的每一个都可以传递给Collections.reverse()

                int[] intArr = { 1, 2, 3, 4, 5 };
                float[] floatArr = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
                double[] doubleArr = { 1.0d, 2.0d, 3.0d, 4.0d, 5.0d };
                byte[] byteArr = { 1, 2, 3, 4, 5 };
                short[] shortArr = { 1, 2, 3, 4, 5 };
                Collections.reverse(Ints.asList(intArr));
                Collections.reverse(Floats.asList(floatArr));
                Collections.reverse(Doubles.asList(doubleArr));
                Collections.reverse(Bytes.asList(byteArr));
                Collections.reverse(Shorts.asList(shortArr));
                System.out.println(Arrays.toString(intArr));
                System.out.println(Arrays.toString(floatArr));
                System.out.println(Arrays.toString(doubleArr));
                System.out.println(Arrays.toString(byteArr));
                System.out.println(Arrays.toString(shortArr));
                

                输出:

                [5、4、3、2、1]
                [5.0、4.0、3.0、2.0、1.0]
                [5.0、4.0、3.0、2.0、1.0]
                [5、4、3、2、1]
                [5, 4, 3, 2, 1]

                【讨论】:

                • Ints.asList(intArr).sort() 创建底层数组的副本并对副本进行排序。
                【解决方案18】:

                如果性能很重要,并且列表通常已经排序得很好。

                冒泡排序应该是最慢的排序方式之一,但我见过性能最佳的情况是简单的双向冒泡排序。

                因此,这可能是您可以从自己编码中受益的少数情况之一。但你真的需要做对(确保至少有其他人确认你的代码,证明它有效等)

                正如其他人指出的那样,从排序数组开始可能会更好,并在更改内容时保持排序。这可能会表现得更好。

                【讨论】:

                  【解决方案19】:

                  您不能使用比较器对原始数组进行排序。

                  您最好的选择是为您的用例实现(或借用实现)排序算法 appropriate 以对数组进行排序(在您的情况下以相反的顺序)。

                  【讨论】:

                    【解决方案20】:

                    我不知道 Java 核心 API 中有任何原始排序工具。

                    从我对D programming language(类固醇上的一种 C)的实验中,我发现合并排序算法可以说是最快的通用排序算法(它是 D 语言本身用来实现它的排序函数)。

                    【讨论】:

                      【解决方案21】:

                      在其他答案中,Arrays.asList 存在一些混淆。如果你说

                      double[] arr = new double[]{6.0, 5.0, 11.0, 7.0};
                      List xs = Arrays.asList(arr);
                      System.out.println(xs.size());  // prints 1
                      

                      那么您将拥有一个包含 1 个元素的列表。生成的 List 将 double[] 数组作为其自己的元素。你想要的是有一个List&lt;Double&gt;,它的元素是double[]的元素。

                      不幸的是,没有涉及比较器的解决方案适用于原始数组。 Arrays.sort 仅在传递Object[] 时接受比较器。由于上述原因,Arrays.asList 不会让您从数组的元素中创建列表。

                      因此,尽管下面的 cmets 引用了我之前的回答,但没有比在排序后手动反转数组更好的方法了。任何其他方法(例如将元素复制到 Double[] 并反向排序并将它们复制回来)会更多代码和更慢。

                      【讨论】:

                      • 不需要道歉。编辑您的答案以使其更好是 SO 的全部意义所在。我什至赞成你的回答,因为它似乎是得到最多讨论的一个。
                      • 不能用 array = double[]{} 编译
                      • 我尝试了与 @jjnguy 和 @Eli 相同的方法 - 我认为 Arrays.asList() 将比手动将 double[] 转换为 Double[] 更高效,这需要对每个元素进行装箱 - 对...
                      • 在编写了一些测试程序后,我已经编辑了答案以反映我的发现。
                      • 我在编写自己的测试程序后才意识到这一点......我认为这里的答案是只使用python,它没有这种愚蠢的原始与对象区别=)。
                      【解决方案22】:

                      您的实现(问题中的那个)比例如更快。用toList() 包装并使用基于比较器的方法。通过比较器方法或包装的 Collections 对象进行自动装箱和运行比仅反转要慢得多。

                      当然,您可以编写自己的排序。这可能不是您要寻找的答案,请注意,如果您经常出现“如果数组已经排序良好”的评论,您最好选择一种可以处理的排序算法这种情况很好(例如插入)而不是使用Arrays.sort()(这是合并排序,或者如果元素数量很少则插入)。

                      【讨论】:

                      • 你不能在 double[] 上调用 Arrays.toList 并让它做你想做的事; Arrays.toList 需要传递一个 Double[] 而不是原始类型的数组。
                      • Arrays.asList(new double[]{});编译正常。
                      • 这就是我的观点;我在新编辑的答案中总结了这个问题。
                      最近更新 更多