2015-10-16 7 views
12

私はC++を3年間学校でコーディングしてきました。私はちょうど2日前にJavaでコーディングを開始しました。私の質問は:一般的な配列を作るために悪い習慣ジェネリックアレイを作るのは悪い習慣ですが、何が代わりになるのでしょうか?

ですか?代わりになるのは何ですか?

私は困惑していると私は、この例のような奇妙な何かをすることのほかに、一般的な配列を作るように見えることはできません。

//Class implementing the MergeSort algorithm with generic types 
// Revised by Doina January 2014 

package Sorting; 

import java.lang.*; 

public class MergeSort { 

    // Wrapper method for the real algorithm 
    // T is the generic type which will be instantiated at runtime 
    // elementas are required to be comparable 
    public static <T extends Comparable<T>> void sort(T[] a) { 
     mergesort(a, 0, a.length - 1); 
    } 

    // Recursive mergesort method, following the pseudocode 
    private static <T extends Comparable<T>> void mergesort(T[] a, int i, int j) { 
     if (j - i < 1) return; 
     int mid = (i + j)/2; 
     mergesort(a, i, mid); 
     mergesort(a, mid + 1, j); 
     merge(a, i, mid, j); 
    } 

    // Merge method 
    // Here we need to allocate a new array, but Java does not allow allocating arrays of a generic type 
    // As a work-around we allocate an array of type Object[] the use type casting 
    // This would usually generate a warning, which is suppressed 
    @SuppressWarnings("unchecked") 
    private static <T extends Comparable<T>> void merge(T[] a, int p, int mid, int q) { 

     Object[] tmp = new Object[q - p + 1]; 
     int i = p; 
     int j = mid + 1; 
     int k = 0; 
     while (i <= mid && j <= q) { 
      if (a[i].compareTo(a[j]) <= 0) 
       tmp[k] = a[i++]; 
      else 
       tmp[k] = a[j++]; 
      k++; 
     } 
     if (i <= mid && j > q) { 
      while (i <= mid) 
       tmp[k++] = a[i++]; 
     } else { 
      while (j <= q) 
       tmp[k++] = a[j++]; 
     } 
     for (k = 0; k < tmp.length; k++) { 
      a[k + p] = (T) (tmp[k]); // this is the line that woudl generate the warning 
     } 
    } 

    // Main methos to test the code, using Integer Objects 
    public static void main(String[] args) { 
     Integer[] a = new Integer[5]; 
     a[0] = new Integer(2); 
     a[1] = new Integer(1); 
     a[2] = new Integer(4); 
     a[3] = new Integer(3); 
     a[4] = new Integer(-1); 

     // T will be instantiated to Integer as a resutl of this call 
     MergeSort.sort(a); 

     // Print the result after the sorting 
     for (int i = 0; i < a.length; i++) 
      System.out.println(a[i].toString()); 
    } 
} 
+5

あなたが変わったと思うマージソートの例は何ですか? – azurefrog

+0

また、http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java – azurefrog

+3

を参照してください。私はあらかじめ気付いていませんでしたが、 'import java.lang。* '全く無意味です。あなたはそれを何かでやっていると捕らえたくありません。 – Makoto

答えて

19

それは、それはそれ自体は悪いアイデアだということではありません。それは単にジェネリックであり、配列はあまりうまく混合しません。

理由は、共分散と不変性によるものです。配列は共変です((List<Integer>IntegerObjectであってもList<Object>ではありません)IntegerObjectですが、一般的なクラスが不変あるためInteger[]Object[]ある。

あなたはまた、未チェックのキャストに対処する必要があります、これは敗北ジェネリック医薬品の全体の目的は、一般的な配列を作成する最も一般的な方法 - 。E[] foo = (E[]) new Object[10]; - 安全な型ではなく、コンパイル時に強制することはできませんそれは、実行時にそれを推論することは可能ですが、コンパイル時。ジェネリック医薬品がテーブルに持って来るのチェックはその時点で失われます。

は直接質問に答えるために、どこで、可能な場合、彼らはジェネリックとうまく非常にをプレイするとして、あなたは、代わりにJava Collectionsを使用します。

ちょうどあなたの供給コードをちらっと見て、私はList<T>代わりのT[]を使用すると、あなたの問題のほとんどが、あなたを得るでしょう(と、それらの操作はリンクリストで高価になることができますので、私はあなたがArrayListを渡していることを望んでいるだろうと想像します)。

+1

ArrayList **は汎用配列を使用していません**。 [これは 'Object []'を使用します。](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#134)また、 'E [] foo =(E [])new Object [10];'はジェネリック配列を作成しません。 'Integer [] a = Thing .methodReturningEArray()'のような非一般的な変数に配列を代入しようとすると、誤ったキャストになります。一般的な配列を作成する唯一の安全な方法は、 'ArrayList.toArray(T [])'のように、別の配列またはクラスオブジェクトを使用して実行時に型情報を取得することです。 – user2357112

+0

あなたは 'ArrayList'ビットについて正しくあります。私はそれを喜んで訂正しますが、特にそのメソッドの' T [] 'へのキャストがあることを考慮して、2番目の部分ではあなたに同意しません。そしてもしあなたが落ちたら(ウサギの穴)(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/Arrays.java#Arrays .CopyOf%28java.lang.Object%5B%5D%2Cint%2Cjava.lang.Class%29)、あなたは場所の*ロット*内の一般的な 'T []'に 'Object []'をキャストしています。したがって、これらのメソッドは安全かもしれませんが、私が記述している規約はライブラリでも使用されています。 – Makoto

+1

'T []'へのキャストがありますが、それはテンプレートが 'T []'をテンプレートとして作成されたため、実際には 'T []'であることが分かっているからです。 'Object []'を新しく作成し、それを 'T []'にキャストすることは全く異なります。 [ここでは、それが失敗するのを見て!](http://ideone.com/sslO80)「ウサギの穴をあけて」という例では、Object []を 'T []'にキャストします。実際には 'T'は' Object'です。 – user2357112

1

まことの答えに追加、私はArraysが原因タイプ情報が原因コンパイル時に型消去に使用できないようジェネリッククラスは不変であるのに対し、彼らの型情報は実行時に利用可能であるという事実に共変であると言うでしょう。

共変アレイ: -

Object[] covariantArrays = new String[5]; 
covariantArrays[0] = new Dog(); // will give java.lang.ArrayStoreException 

不変配列: - ジェネリックは、型の安全性とアレイをコンパイルするために限られているとして、一般的な配列がうまく行っていない。このため

List invariantArrays = new List<String>(); 
invariantArrays.add(new Dog()); // Works fine as type information is not available 

ランタイムでも実際のタイプの情報が得られます

4

これは悪いことではありません通常の配列を作成することはできますが、正しく行うことは、煩雑な人が通常避けることです。

なぜなら、厄介なのは、配列が整形されている間にジェネリックが消去されるからです。つまり、型パラメーターはコンパイル時に消去され、配列のコンポーネントタイプは保持されます。したがって、ランタイムはすべての配列のコンポーネントタイプを知っていますが、すべてのオブジェクトの型引数、ライン

E[] array = new E[10]; 

は、ランタイムが新しい配列のコンポーネント・タイプを知っておく必要があるため、コンパイルされませんが、忘れてしまったEがされました。

まことの答えでの回避策:それは実際にObject[]を作成したが、その後E[]あるコンパイラにふりとして

E[] array = (E[]) new Object[10]; 

は、良いアイデアではありません。ランタイムが忘れてしまったのはEです。この型は、型が正しくないにもかかわらず実行時にも成功します。しかし、ランタイムは、できるだけ早く追加検査を行うことによって、すなわちオブジェクトがタイプが汎用ではない変数に格納されているときに、メモリの安全性を強制する。例えば:

ある
static <E> E[] createArray(int size) { 
    return (E[]) new Object[size]; 
} 

public static void main(String[] args) { 
    String[] array = createArray(size); // throws ClassCastException 
    for (String s : array) { 
     // whatever 
    } 
} 

、この回避策は、特定の状況でのみ動作することをハックし、それ以外の場合は非常に不可解な行動(キャストが含まれていないコードの行でClassCastExceptionが...)が発生しますです。

E[]を作成するための唯一の方法は、私たちの希望コンポーネントタイプのクラスオブジェクトを提供することで、反射によってである:

Class<E> eClass = ...; 
E[] array = Arrays.newInstance(eClass, 10); 

が、どのように我々は、このクラスのオブジェクトを取得することができますか?呼び出し元が知っている場合は、クラスリテラル(Integer.classなど)を渡すことも、別のオブジェクトでリフレクションを使用することもできます。あなたのケースでは、あなたは別のE[]手元に持っているので、あなたは何であるかEその配列を依頼することができます。

E[] originalArray = ...; 
Class<E> eClass = (Class<E>) originalArray.getClass().getComponentType(); 
E[] newArray = (E[]) Array.newInstance(eClass, size); 

これは、新しい配列が誰かいない限り、E[]ある古いものと同じ型のものであることを確認しますMakotoの回避策を使ってその配列のタイプについて私たちに嘘をつきました。

ご覧のとおり、一般的なアレイを作成することは可能ですが、通常は人々がそれを避けるために非常に長持ちするのは面倒です。通常は、スーパータイプの配列を使用しています(マージソートではComparable[]はキャストする必要がないため、Object[]よりもうまくいくかもしれません)。代わりにArrayListを使用しています。

関連する問題