(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
(1)では、Listインターフェースの実装を入れ替えることができます。 (1)は通常、必要性に関係なくアプリケーションで使用されているようです(私自身は常にこれを使用しています)。
誰かが(2)を使っているのかどうか疑問に思いますか?
また、実際には(2)に対して(1)を使用することが実際にどのくらいの頻度で(そして例を挙げてください)(つまり、(2)では足りない場合があります。)_をインタフェースにコーディングおよびbest practiceなど)
ほとんどの場合、最初のものが2番目のものよりも優先されます。 1つ目は、残りのコードに影響を与えることなく、List
の実装を(たとえば LinkedList
に)変更できるという利点があります。どこでもArrayList
をArrayList
に変更する必要があるだけでなく、LinkedList
特有のメソッドを使用したかもしれないので、これはArrayList
を処理するのが難しいタスクになります。
List
の実装について読むことができます ここ 。あなたはArrayList
で始めるかもしれませんが、その直後に別の実装がより適切であることを発見します。
誰かが(2)を使っているのかどうか疑問に思いますか?
はい。しかし、稀な理由(IMO)のためにめったにありません。
そしてArrayList
を使うべきだったときにList
を使っていたので人々はやけどをします:
Collections.singletonList(...)
やArrays.asList(...)
のようなユーティリティメソッドはArrayList
を返しません。
List
APIのメソッドは、同じ型のリストを返すことを保証しません。
たとえば、誰かがやけどをした、 https://stackoverflow.com/a/1481123/139985ArrayList.sublist(...)
がArrayList
を返さないため、ポスターに「スライス」の問題がありました。彼のすべてのリスト変数の型としてArrayList
を使うこと。彼はサブリストを新しいArrayList
にコピーすることで問題を「解決」しました。
List
がどのように振る舞うのかを知るために必要な引数は、主にRandomAccess
マーカーインタフェースを使用して対処します。はい、それは少し不格好ですが、代替案はもっと悪いです。
また、実際にどのくらいの頻度で実際に(2)より(1)を使用する必要があるか(つまり、(2)で十分ではない場合。「インターフェイスへのコーディング」およびベストプラクティスなど)。
質問の「どのくらいの頻度で」の部分は客観的に解決できないものです。
(そして例を挙げてください)
場合によっては、アプリケーションによっては、ArrayList
APIのnotであるList
APIのメソッドを使用する必要があります。たとえば、ensureCapacity(int)
、trimToSize()
、removeRange(int, int)
などです。 (そして最後のものは、メソッドがpublic
であると宣言するArrayListのサブタイプを作成した場合にのみ発生します。)
それが、IMOというインターフェースではなくクラスにコーディングする唯一の正当な理由です。
(特定の状況下では...一部のプラットフォームではパフォーマンスがわずかに向上する可能性があることは理論的には可能です...ただし、最後の0.05%が本当に必要でない限り、これを実行する価値はありません。健全な理由、IMO)
ランダムアクセスが効率的かどうかわからない場合は、効率的なコードを書くことはできません。
それが妥当な点です。しかし、Javaはそれに対処するためのより良い方法を提供します。例えば.
public <T extends List & RandomAccess> void test(T list) {
// do stuff
}
RandomAccess
を実装していないリストでそれを呼び出すと、コンパイルエラーになります。
静的型付けが面倒な場合は、instanceof
...を使用して動的にテストすることもできます。また、リストがランダムアクセスをサポートしているかどうかに応じて、(動的に)異なるアルゴリズムを使用するようにコードを書くこともできます。
ArrayList
がRandomAccess
を実装する唯一のリストクラスではないことに注意してください。その他にはCopyOnWriteList
、Stack
、Vector
があります。
私は人々がSerializable
について同じ議論をしているのを見ました(List
はそれを実装していないので)...しかし、上記のアプローチはこの問題も解決します。 (ランタイム型を使用している場合は、まったく解決可能です。ArrayList
は、要素が直列化可能でない場合、直列化に失敗します。
例えば、LinkedList
があなたのアプリケーションに最適な選択であると決めるかもしれません、しかし、後でArrayList
がパフォーマンス上の理由からより良い選択であるかもしれないと決めます。
つかいます:
List list = new ArrayList(100); // will be better also to set the initial capacity of a collection
の代わりに:
ArrayList list = new ArrayList();
参考のため:
(主にコレクション図に掲載)
Set型の変数にHashSet
またはTreeSet
への参照を格納することは、 正しいスタイルと見なされます です。
Set<String> names = new HashSet<String>();
こうすれば、代わりにTreeSet
を使用することにした場合、1行だけ変更する必要があります。
また、集合を操作するメソッドはSet型のパラメータを指定する必要があります。
public static void print(Set<String> s)
それから このメソッドはすべての集合の実装に使用できる 。
理論的には、リンクリストについても同じことを推奨する必要があります。つまり、LinkedList参照をList型の変数に保存することです。ただし、Javaライブラリでは、ListインタフェースはArrayList
クラスとLinkedList
クラスの両方に共通です。特に、リンクリストには非常に非効率的ですが、ランダムアクセス用のgetおよびsetメソッドがあります。
ランダムアクセスが効率的かどうかわからない場合は、 効率的なコードを記述できません 。
これは明らかに標準ライブラリの重大な設計エラーであり、そのためListインタフェースを使用することはお勧めできません。
そのエラーがどれほど厄介であるかを見るために、 Collections クラスのbinarySearch
メソッドのソースコードを見てください。このメソッドはListパラメータを取りますが、バイナリ検索ではリンクリストには意味がありません。それからコードは不器用にそのリストがリンクされたリストであるかどうかを発見しようと試みてそれから線形検索に切り替わります!
Set
インターフェースとMap
インターフェースはうまく設計されていますので、それらを使用してください。
Codeがリストの「所有者」の場合は、(2)を使用します。これは、例えばローカルのみの変数に当てはまります。 List
ではなく抽象型ArrayList
を使用する理由はありません。所有権を証明するもう1つの例:
public class Test {
// This object is the owner of strings, so use the concrete type.
private final ArrayList<String> strings = new ArrayList<>();
// This object uses the argument but doesn't own it, so use abstract type.
public void addStrings(List<String> add) {
strings.addAll(add);
}
// Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
public List<String> getStrings() {
return Collections.unmodifiableList(strings);
}
// Here we create a new list and give ownership to the caller. Use concrete type.
public ArrayList<String> getStringsCopy() {
return new ArrayList<>(strings);
}
}
あなたがList
を書くとき、あなたはあなたのオブジェクトがList
インターフェースだけを実装することを実際に言う、しかしあなたはあなたのオブジェクトがどのクラスに属するかを指定しない。
ArrayList
を書くとき、あなたはあなたのオブジェクトクラスがサイズ変更可能な配列であることを指定します。
そのため、最初のバージョンでは将来的にコードがより柔軟になります。
Javaのドキュメントを見てください。
クラスArrayList
- List
インタフェースのサイズ変更可能な配列の実装。
インタフェースList
- 順序付きコレクション(シーケンスとも呼ばれる)。このインタフェースのユーザは、リストの各要素が挿入される場所を正確に制御できます。
Array
- 単一型の固定数の値を保持するコンテナオブジェクト。
(3)コレクションmyCollection = new ArrayList();
私はこれを普通に使っています。そして only Listメソッドが必要な場合は、Listを使います。 ArrayListと同じです。あなたはいつももっと "狭い"インターフェースに切り替えることができますが、もっと "広い"に切り替えることはできません。
私は(2)を使う人々は Liskov置換の原則 または 依存性の逆転の原理 を知らないと思う。あるいは彼らは本当にArrayList
を使わなければなりません。
実際には(2)が望ましいだけでなく必須である場合もあります、そして私は非常に驚いています。
連載!
直列化可能クラスがあり、それにリストを含める場合は、ArrayList
インターフェースはJava.io.Serializable
を拡張しないため、フィールドをList
のような具象で直列化可能な型にする必要があります。
明らかに、ほとんどの人は直列化を必要とせず、これを忘れています。
例:
public class ExampleData implements Java.io.Serializable {
// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();
次の2つのうち
(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
第一が一般的に好ましい。 List
インターフェースからのメソッドのみを使用するので、他のList
の実装を自由に使用できます。将来的にはLinkedList
。だからそれはあなたを特定の実装から切り離します。今言及する価値がある2つのポイントがあります:
誰かが使用するかどうか私は思っています
たまにしか読みません。 ArrayList
の実装の一部であるがインタフェースList
の一部ではないメソッドが必要なとき。例えばensureCapacity
です。
また、実際にどのくらいの頻度で(そして例を挙げてください)実際に(1)を(2)の使用が必要になるのでしょうか。
ほとんど常にあなたはオプション(1)を好む。これはOOPの古典的なデザインパターンで、コードを特定の実装やプログラムからインタフェースに分離することを常に試みています。
誰かがこれをもう一度(二重に)尋ねたので、私はこの問題についてもう少し詳しく説明しました。
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
ArrayList<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("b");
}
バイトコードビューアを使用する場合(私は http://asm.ow2.org/Eclipse/index.html を使用しました)、 list snippetについては以下を参照してください(リストの初期化と割り当てのみ)。 :
L0
LINENUMBER 9 L0
NEW ArrayList
DUP
INVOKESPECIAL ArrayList.<init> () : void
ASTORE 1
L1
LINENUMBER 10 L1
ALOAD 1: list
LDC "a"
INVOKEINTERFACE List.add (Object) : boolean
POP
L2
LINENUMBER 11 L2
ALOAD 1: list
LDC "b"
INVOKEINTERFACE List.add (Object) : boolean
POP
そして alist の場合:
L3
LINENUMBER 13 L3
NEW Java/util/ArrayList
DUP
INVOKESPECIAL Java/util/ArrayList.<init> ()V
ASTORE 2
L4
LINENUMBER 14 L4
ALOAD 2
LDC "a"
INVOKEVIRTUAL Java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
L5
LINENUMBER 15 L5
ALOAD 2
LDC "b"
INVOKEVIRTUAL Java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
違いは、 list が _ invokeinterface _ を呼び出すことになり、 aList calls _ invokevirtual _ となることです。 Bycode Outline Pluginへの参照
invokeinterfaceは、Javaインタフェース内で宣言されたメソッドを呼び出すために使用されます。
仮想呼び出し中
インタフェースメソッド(invokeinterfaceを使用)、静的メソッド(invokestaticを使用)、およびinvokespecialで処理されるいくつかの特別な場合を除く、すべてのメソッドを呼び出します。
要約すると、invokeinterfaceの間はinvokevirtual pops objectref がスタックをオフになります。
インタプリタはオペランドスタックから 'n'項目をポップします。 'n'はバイトコードから取られた8ビットの符号なし整数パラメータです。これらの項目の最初のものはobjectrefで、メソッドが呼び出されているオブジェクトへの参照です。
私がこれを正しく理解していれば、違いは基本的にそれぞれの方法で objectref を取得する方法です。
Listはインタフェースです。メソッドはありません。 List参照でmethodを呼び出すときどちらの場合も、実際にはArrayListのメソッドを呼び出します。
そして将来的にはList obj = new ArrayList<>
をList obj = new LinkList<>
または Listインターフェースを実装する他の型に変更することができます。
私が知っているのは、(2) が - になる可能性があることを知っている唯一のケースです。GWTを使用するときだけです。しかし、JVM(1)の内部で実行されている通常のJavaでは、おそらく常に優れています。
1が優先されない限り、私は言うと思います
私の推測では、99%のケースではListを使用することができますが、これが推奨されます。
removeAll
、またはadd(null)
List
インターフェースには、いくつかの異なるクラスがあります - ArrayList
とLinkedList
。 LinkedList
はインデックス付きコレクションを作成するために使用され、ArrayList
- ソートされたリストを作成するために使用されます。それで、あなたはそれを引数に使うことができますが、あなたが使うものだけではなく、あなたのコードやライブラリなどを使う他の開発者が異なる種類のリストを使うことを許可することができます。
ArrayList<Object> myMethod (ArrayList<Object> input) {
// body
}
ArrayList
ではなくLinkedList
でのみ使用できますが、メソッドが使用されている他の場所ではList
クラスのいずれかを使用することができます。これは単なる選択であるため、インターフェイスを使用すると許可できます。
List<Object> myMethod (List<Object> input) {
// body
}
このメソッドの引数では、使用したいList
クラスをどれでも使用できます。
List<Object> list = new ArrayList<Object> ();
list.add ("string");
myMethod (list);
結論:
可能であればどこでもインターフェースを使用してください。あなたや他の人が使用したい異なる方法を使用するように制限しないでください。