JavaArrayList<E>
実装では、オブジェクトの配列に基づいています。
実装がデータストレージの代わりに配列を使用する
理由を誰かが私に説明できますか?使用する利点は何ですか?ArrayList<E>
Object[]
E[]
Object[]
3 に答える
Javaでは、ジェネリック型の配列を作成するのは簡単ではありません。
単純なアプローチはコンパイルされません:
public class Container<E> {
E[] arr = new E[3]; // ERROR: Cannot create a generic array of E
}
に置き換えれE
ばObject
、すべてうまくいきます(コンテナ実装の他の場所で複雑さが増すという犠牲を払って)。
別のアプローチもありますが、それらは異なるトレードオフのセットを提示します。詳細については、Javaでジェネリック配列を作成する方法を参照してください。
したがって、まず、配列オブジェクトの実際の実行時タイプはである必要があることに注意してObject[]
ください。これは、配列が実行時にコンポーネントタイプを認識しているため(実際には、異なる配列タイプは実行時に異なるタイプです)、配列の作成時にコンポーネントタイプを指定する必要がありますが、ArrayList
オブジェクトは実行時にそのタイプ引数を認識しません。
とはいえ、インスタンス変数のコンパイル時の型は、またはのいずれObject[]
かとして宣言できますがE[]
、さまざまな長所と短所があります。
次のように宣言されている場合Object[]
:
private Object[] arr;
// to create it:
arr = new Object[3];
// to get an element:
E get(int i) { return (E)arr[i]; }
これの欠点は、E
何かを取り出すたびにキャストする必要があることです。つまり、基本的にプレジェネリックコンテナとして使用しているということです。
次のように宣言されている場合E[]
:
private E[] arr;
// to create it:
arr = (E[])new Object[3];
// to get an element:
E get(int i) { return arr[i]; }
これの利点は、物事を取り出すときにキャストする必要がなくなることです。これによりarr
、汎用コンテナーのように、の使用に関するタイプチェックが提供されます。欠点は、論理的には、キャストが嘘であるということです。実行時型がであるオブジェクトを作成したことがわかっているため、がである場合を除き、それはObject[]
のインスタンスではありません。E[]
E
Object
ただし、クラスのインスタンスメソッド内にE
消去されるため、これを実行してもすぐに問題はありません。Object
問題が発生する可能性がある唯一の方法は、オブジェクトがクラスの外部に何らかの形で公開されている場合(たとえば、メソッドで返される、パブリックフィールドに配置されるなど)、そのタイプをE[]
次のように使用する場合です(そうではありません)。
// This would be bad. It would cause a class cast exception at the call site
E[] getArray() { return arr; }
しかしArrayList
、そして実際に適切に設計されたコンテナクラスは、内部配列などの実装の詳細を外部に公開することはありません。とりわけ、それは抽象化を破るでしょう。このクラスの作成者がこの配列を公開しないことを認識している限り、この方法で問題は発生せず(コードを見て気付かない次の人を混乱させる可能性があります)、自由に使用できます。この方法がもたらすタイプチェックの増加の利点。
型消去(つまり、例のようなジェネリック型パラメーターがコンパイル型で削除されるという事実)を考慮E
すると、生成されるバイトコードはどちらの場合も類似していると思います。
メンテナンスの観点から、Objectの代わりにtypeパラメーターを使用すると、コードが読みやすくなります(キャストが制限されるため)。しかし、のAPIはArrayList
その「生の」Object
配列を公開しないので、単なるJava開発者にとっては何の違いもないと思います:)