60

我有大量的原始类型(双)。如何按降序对元素进行排序

不幸的是,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;
}

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

有什么更好的选择?

4

22 回答 22

22

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

是的,我看到了“下降”部分。排序是困难的部分,您希望从 Java 库代码的简单性和速度中受益。完成后,您只需反转数组,这是一个相对便宜的 O(n) 操作。这是我发现的一些代码,只需 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;
}
于 2008-10-20T03:47:31.640 回答
21

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 内置的双轴快速排序的简单编辑。这意味着速度应该接近内置排序的速度。

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

于 2014-11-24T00:03:18.510 回答
10

这是一个单行代码,使用 Java 8 中的流

int arr = new int[]{1,2,3,4,5};
Arrays.stream(arr).boxed().sorted(Collections.reverseOrder()).mapToInt(Integer::intValue).toArray();
于 2019-10-21T15:03:34.483 回答
8

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]

于 2011-09-14T10:33:00.087 回答
2

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

  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
    }
}
于 2016-04-15T09:53:27.580 回答
1

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

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

于 2008-10-18T17:00:57.010 回答
1

其他答案有些混乱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<Double>其元素是double[].

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

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

于 2008-10-18T17:02:44.387 回答
1

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

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

于 2008-12-11T04:30:26.333 回答
1

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

于 2014-10-22T07:02:01.200 回答
1
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];
于 2016-11-07T09:23:39.567 回答
0

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

从我对D 编程语言(一种类 C 的类固醇)的实验中,我发现合并排序算法可以说是最快的通用排序算法(它是 D 语言本身用来实现其排序功能的) .

于 2008-10-18T18:43:05.543 回答
0

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

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;
}
于 2008-12-11T05:50:37.140 回答
0

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

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

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

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

于 2009-01-08T09:31:52.767 回答
0

对于小型阵列,这可能有效。

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!
于 2016-08-23T13:43:31.703 回答
0

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

double[] nums = Arrays.stream(nums).boxed().
        .sorted((i1, i2) -> Double.compare(i2, i1))
        .mapToDouble(Double::doubleValue)
        .toArray();
于 2017-01-28T03:20:58.983 回答
0

在 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]

于 2018-02-26T20:56:48.783 回答
0

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

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);
于 2018-10-10T09:45:22.433 回答
0
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
于 2018-11-19T17:33:02.537 回答
0

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

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);
    }
}
于 2020-10-02T08:32:16.557 回答
-1
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()

于 2014-07-31T23:24:02.967 回答
-1

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

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

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

有关详细说明,请在此处查看我的博客文章。

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;
}
于 2017-10-10T19:58:11.640 回答
-2
double[] array = new double[1048576];

...

默认顺序是升序

颠倒顺序

Arrays.sort(array,Collections.reverseOrder());
于 2009-01-08T08:43:45.000 回答