web-dev-qa-db-ja.com

プリミティブ型の配列を降順に並べ替える

プリミティブ型(double)の大きな配列を持っています。 降順で要素をソートするにはどうすればよいですか?

残念ながらJava APIは、Comparatorによるプリミティブ型のソートをサポートしていません。

1つの回避策は、ソートしてから逆にすることです。

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;
}

これは時間がかかります-特に配列が既に十分にソートされている場合。

より良い代替手段は何ですか?

45

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の3番目の引数としてsortを渡すと、不安定なソート、Javaの組み込み dual-pivot quicksort の簡単な編集が使用されます。これは、速度が組み込みのソートの速度に近いことを意味します。

完全な開示:Java Primitive library。

17
Brandon Mintern

ホイールを再発明して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;
}
16
user29480

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]

8

私はまだ最も簡単な解決策だと思います:

  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
    }
}
2
madfree

プリミティブ配列のソートにコンパレーターを使用することはできません。

あなたの最善の策は、ユースケースで配列をソートするための 適切な であるソートアルゴリズムの実装(または実装の借用)です(あなたの場合は逆の順序で)。

1

数値型では、ソートの前後の要素を否定することはオプションのようです。ソート後の単一のリバースに関連する速度はキャッシュに依存し、リバースがより速くない場合、ノイズの差が失われる可能性があります。

1
greybeard
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];
1
Shravan Kumar
double[] array = new double[1048576];

...

デフォルトでは、順序は昇順です

順序を逆にするには

Arrays.sort(array,Collections.reverseOrder());
1
Piyush Doifode

実装(問題の実装)は、たとえばtoList()でラップし、コンパレータベースのメソッドを使用します。コンパレータメソッドまたはラップされたコレクションオブジェクトの自動ボックス化および実行は、単に逆にするよりもはるかに遅くなります。

もちろん、独自のソートを書くこともできます。それはあなたが探している答えではないかもしれませんが、しかし「配列がすでにかなりうまくソートされている場合」に関するコメントが頻繁に起こる場合、 Arrays.sort()(mergesort、または要素数が少ない場合は挿入)を使用するよりも、そのケースを適切に処理する(挿入など)ソートアルゴリズムを選択するとよいでしょう。

1
Jason Cohen

他の回答では、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つの要素を持つリストが作成されます。結果のリストには、独自の要素としてdouble []配列が含まれます。必要なのは、その要素がList<Double>の要素であるdouble[]を持つことです。

残念ながら、コンパレータを含むソリューションはプリミティブ配列に対して機能しません。 Arrays.sortは、Object[]が渡される場合にのみコンパレータを受け入れます。また、上記の理由により、Arrays.asListでは配列の要素からListを作成できません。

したがって、以下のコメントが参照する以前の回答にもかかわらず、ソート後に配列を手動で反転するよりも良い方法はありません。他のアプローチ(要素をDouble[]にコピーし、逆ソートしてそれらをコピーするなど)は、コードが多くなり、速度が低下します。

1
Eli Courtwright

パフォーマンスが重要であり、通常、リストはすでにかなり適切にソートされている場合。

バブルソートはソートの最も遅い方法の1つである必要がありますが、最高のパフォーマンスが単純な双方向のバブルソートである場合を見てきました。

したがって、これは自分でコーディングすることでメリットが得られる数少ないケースの1つかもしれません。しかし、あなたは本当にそれを正しくする必要があります(少なくとも他の誰かがあなたのコードを確認し、それが機能することを証明するなどを確認してください)

他の誰かが指摘したように、ソートされた配列から始めて、内容を変更する間、ソートされたままにしておく方がさらに良いかもしれません。それはさらに良くなるかもしれません。

0
myplacedk

以下は私のソリューションです。あなたのニーズに合わせて調整することができます。

どのように機能しますか?引数として整数の配列を取ります。その後、引数からの配列と同じ値を含む新しい配列を作成します。これを行う理由は、元の配列をそのまま残すためです。

新しい配列にコピーされたデータが含まれたら、条件if(newArr [i] <newArr [i + 1])がfalseと評価されるまで値を交換してソートします。つまり、配列は降順でソートされます。

完全な説明については、私のブログ投稿を確認してください 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;
}
0
Catalin Pit
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
0
Shubham Gaur

Java8を使用している場合は、配列をストリームに変換し、並べ替えて元に戻します。すべてのタスクを1行で実行できるため、この方法はそれほど悪くないと思います。

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

JavaコアAPI内の基本的なソート機能については知りません。

Dプログラミング言語 (ステロイドのCの一種)を使った実験から、マージソートアルゴリズムはほぼ間違いなく最速の汎用ソートアルゴリズムであることがわかりました(D言語そのものです)ソート機能の実装に使用します)。

0
Leo

アルゴリズムは正しいです。しかし、次のように最適化を行うことができます:反転中に、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;
}
0
lakshmanaraj

小さい配列の場合、これは機能する可能性があります。

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!
0
user462990

それは非常に古い投稿であることを理解していますが、私はプリミティブな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);
0
MJA