web-dev-qa-db-ja.com

EnumerationがJava.utilsでListではなくArrayListに変換されるのはなぜですか?

Java.utilsパッケージの Collections.list() メソッドがArrayList<T>ではなくList<T>を返すという正当な理由はありますか?

明らかにArrayListListですが、実装タイプではなくインターフェースタイプを返すことは一般的には良い習慣であるという印象を受けています。

74
Tianxiang Xiong

免責事項:私はJDKの作成者ではありません。

自分のコードをインターフェイスに書き込むのは正しいことですが、mutableコレクションを返す場合はサードパーティの場合は、どのようなListが返ってくるのかをサードパーティに知らせることが重要です。

LinkedListArrayListは、さまざまな操作でパフォーマンスが大きく異なります。たとえば、ArrayListの最初の要素を削除するとO(n)になりますが、LinkedListの最初の要素を削除するとO(1)になります。

戻り値の型を完全に指定することにより、JDKの作成者は、明確なコードで追加情報を伝えるを使用して、どのような種類のオブジェクトが返されるかを確認できるため、このメソッドを適切に使用するコードを記述できます。 。 LinkedListが本当に必要な場合は、ここで指定する必要があります。

最後に、実装を介してインターフェイスにコーディングする主な理由は、実装が変更されると考える場合です。 JDKの作成者は、おそらくこのメソッドを決して変更しないと考えています。 LinkedListまたはCollections.UnmodifiableListを返すことは決してありません。ただし、ほとんどの場合、おそらく次のようにします。

List<T> list = Collections.list(enumeration);
54
durron597

Listを返すときは、プログラムをインターフェイスに昇格させます。これは非常に良い方法です。ただし、このアプローチには制限があります。たとえば、ArrayListに定義されていて、Listインターフェースに存在しないメソッドを使用することはできません-詳細は this answer を参照してください。

Java™チュートリアルの API Design を引用しています。

...任意のタイプのオブジェクトを返すことは問題ありませんimplementsまたはextendsコレクションインターフェースの1つ。これは、インターフェースの1つにすることも、これらのインターフェースの1つを拡張または実装する特殊用途タイプにすることもできます。

..ある意味では、戻り値は入力パラメーターの反対の振る舞いを持つべきです:それはbestであり、一般。たとえば、常にSortedMapを返すことが確実な場合は、関連するメソッドにSortedMapではなくMapの戻り値の型を指定する必要があります。 SortedMapインスタンスは、通常のMapインスタンスよりもビルドに時間がかかり、より強力です。モジュールがすでにSortedMapを構築するために時間を費やしていることを考えると、ユーザーにその増加したパワーへのアクセスを与えることは理にかなっています。さらに、ユーザーは、返されたオブジェクトを、SortedMapを要求するメソッドだけでなく、Mapを受け入れるメソッドにも渡すことができます。

ArrayListは基本的に配列であるため、「コレクション配列」が必要な場合は、これらが最初の選択肢です。したがって、列挙型をリストに変換する場合、私の選択は配列リストになります。

それ以外の場合でも、次のように記述しても有効です。

List<T> list = Collections.list(e);
31
Maroun

新しく作成された可変オブジェクトの「排他的所有権」を返す関数は、多くの場合、最も具体的なタイプである必要があります。不変オブジェクトを返すオブジェクトは、特にそれらが共有される可能性がある場合は、それほど限定的でないタイプを返す必要があります。

区別の理由は、前者の場合、オブジェクトは常に指定されたタイプの新しいオブジェクトを生成できることであり、受信者はオブジェクトを所有し、受信者が実行したいアクションがわからないためです。通常、オブジェクトを返すコードが、代替のインターフェース実装が受信者のニーズを満たすことができるかどうかを知る方法はありません。

後者の場合、オブジェクトが不変であるという事実は、関数が、より複雑な型が実行できるすべてのことを実行できる代替型を識別できることを意味しますその正確な内容を考慮して。たとえば、Immutable2dMatrixインターフェースは、ImmutableArrayBacked2dMatrixクラスとImmutableDiagonal2dMatrixクラスによって実装される場合があります。正方形Immutable2dMatrixを返すことになっている関数は、主対角線からのすべての要素が偶然ゼロの場合はImmutableDiagonalMatrixインスタンスを返し、そうでない場合はImmutableArrayBackedMatrixを返すことを決定できます。前者のタイプは、はるかに少ないストレージ容量を必要としますが、受信者はそれらの違いを気にする必要はありません。

具体的なImmutableArrayBackedMatrixではなくImmutable2dMatrixを返すと、コードは配列に含まれる内容に基づいて戻り値の型を選択できます。また、配列を返すことになっているコードがImmutable2dMatrixの適切な実装を保持している場合、新しいインスタンスを構築する必要がなく、単にそれを返すことができます。不変オブジェクトを操作する場合、これらの要素はどちらも大きな「利点」になります。

ただし、可変オブジェクトを操作する場合は、どちらの要素も関係しません。可変配列が生成されたときに主対角線から外れた要素がない可能性があるという事実は、そのような要素が決してないことを意味しません。したがって、ImmutableDiagonalMatrixは事実上Immutable2dMatrixのサブタイプですが、MutableDiagonalMatrixMutable2dMatrixのサブタイプではありません。後者はメインからのストアを受け入れることができるためです。前者はできませんが対角線。さらに、不変オブジェクトは共有できることが多く、共有する必要がありますが、可変オブジェクトは一般に共有できません。特定のコンテンツで初期化された新しい可変コレクションを要求される関数は、バッキングストアが要求されているタイプと一致するかどうかに関係なく、新しいコレクションを作成する必要があります。

6
supercat

オブジェクトで直接メソッドを呼び出すのではなく、インターフェイスのメソッドを呼び出すときにsmallオーバーヘッドがあります。

このオーバーヘッドは、多くの場合、1つまたは2つのプロセッサ命令にすぎません。メソッドがfinalであることをJITが認識している場合、メソッドを呼び出すオーバーヘッドはさらに低くなります。これはほとんどのコードでは測定できませんが、Java.utilsの低レベルのメソッドでは、問題のある一部のコードで使用される可能性があります。

また、他の回答で指摘されているように、返されるオブジェクトの具体的なタイプは(インターフェースの背後に隠されている場合でも)、それを使用するコードのパフォーマンスに影響します。このパフォーマンスの変化は非常に大きくなる可能性があるため、呼び出し側のソフトウェアが機能しなくなる可能性があります。

明らかにJava.utilsの作成者は、Collections.list()を呼び出すすべてのソフトウェアがその結果に対して何を行うかを知る方法がなく、Collections.list()の埋め込みを変更した場合にこのソフトウェアを再テストする方法はありません。 そのため、型システムで許可されている場合でも、Collections.list()の埋め込みを変更して別のタイプのリストを返すことはありません!

独自のソフトウェアを作成するときは、(うまくいけば)すべてのコードをカバーする自動テストがあり、コードがどのように相互に関連しているかをよく理解していると、パフォーマンスの問題がどこにあるかがわかります。ソフトウェアの設計が変更されている間、呼び出し元を変更せずにメソッドを変更できることは大きな価値があります。

したがって、2セットのトレードオフは大きく異なります。

1
Ian Ringrose