List.ofとArrays.asListの違いは何ですか?


117

Java 9はリストの新しいファクトリーメソッドを導入しましたList.of

List<String> strings = List.of("first", "second");

以前のオプションと新しいオプションの違いは何ですか?つまり、これの違いは何ですか?

Arrays.asList(1, 2, 3);

この:

List.of(1, 2, 3);

1
スチュアート「ビーカー」マークスによるこの講演もご覧ください。
user1803551 2017年

20
@ user1803551私はあなたの不満を理解していますが、この推論は本当に望ましくない先例を作るかもしれません。ここの多くの質問には、「明確に述べられている」答えがあります(これをどのように定義するかによって異なります)。このディスカッションをメタに持ってくることをお勧めしますが、そのようなディスカッションがすでに存在しているはずです(そして誰かが見つけてリンクできることを願っています:-)
Dimitris Fasarakis Hilliard

4
@ user1803551 Javadocは、これら2つのメソッドの実装の詳細の違い(スペースの消費やパフォーマンスなど)について言及していません。これらの詳細も知りたいと思います。
ZhekaKozlov 2017年

5
@ZhekaKozlov受け入れられ、非常に賛成された答えはどちらでもありません。承認された基準についてそれはあなたに何を伝えますか?ドキュメントよりも情報が少ない(シリアル化、ID、順序付け)。どちらかといえば、OpenJDKにリクエストを提出してその情報を追加してください。
user1803551 2017年

3
この質問はmetaで議論されています。
Dimitris Fasarakis Hilliard 2017

回答:


173

Arrays.asListによって返されるリストList.of不変である一方で、変更可能なリストを返します。

List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK

List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException

Arrays.asListnull要素List.ofは許可しますが、以下は許可しません。

List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException

contains nullの場合の動作は異なります。

List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false

List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException

Arrays.asList渡された配列のビューを返すため、配列への変更はリストにも反映されます。以下のためにList.ofこれは本当ではありません。

Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // Prints [1, 10, 3]

Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // Prints [1, 2, 3]

22
リストの構成方法に基づいてリストの動作が異なるのは、オブジェクト指向ではないようです。たぶん、List.ofがImmutableList型を返した場合、これは理にかなっています。これは、ここでは非常に漏れやすい抽象化です。
Sandy Chapman

5
私はJava開発者ではないので、ちょっと見てみてください。おそらく動作が異なるのには十分な理由がありますが、例のようにList <Integer>を返すメソッドがあった場合、インターフェイスをチェックしてもランタイム例外が発生するかどうかを知るには十分ではありません。 nullの場合。同様に、そのメソッド実装の変更は、他の場所でチェックが行われた場合、私のメソッドの呼び出しサイトから離れたコードに影響を与える可能性があります。@Nicolai
Sandy Chapman

8
@SandyChapmanこれは一部(または大部分?)にとって予期しない動作になる可能性がありますが、動作は文書化されています。以下からList.contains(Object o)のjavadocの: 『[...] NullPointerExceptionがスロー-指定された要素がnullで、このリストである場合には(オプション)null要素を許可しません』。または、インターフェースの長い紹介から、「コレクションの実装によっては、それらに含まれる可能性のある要素に制限がある」
Aaron

11
@Aaronは、少なくともドキュメント化されたリークのある抽象化です:)
Sandy Chapman

6
@Sandy Chapman:一部のタイプを返しますList.of ImmutableList、実際の名前は非公開の実装の詳細にすぎません。それが公開されていて、誰かがList再度キャストした場合、どこが違うのですか?またはを試行したときに例外をスローするArrays.asList非パブリックList実装を返すとの違い、またはによって返されるリストで変更がまったく許可されないのはどこですか?それはすべて、インターフェイスで指定されたコントラクトについてです。オプションのメソッドを備えたコレクションインターフェイスは、Java 1.2以降常に不純なOOPでした...addremoveCollections.unmodifiableListList
Holger

31

間の違いArrays.asListList.of

JavaDocsと、Stuart Marks(またはその以前のバージョン)によるこの講演を参照してください

次のコード例を使用します。

List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);

構造的不変性(または:変更不可)

構造的に変更しようとするList.ofと、が発生しますUnsupportedOperationException。これには、追加設定削除などの操作が含まれます。ただし、リスト内のオブジェクトの内容を変更することができるため(オブジェクトが不変でない場合)、リストは「完全に不変」ではありません。

これは、で作成された変更不可能なリストの運命と同じCollections.unmodifiableListです。このリストのみが元のリストのビューであるため、元のリストを変更すると変更される可能性があります。

Arrays.asListは完全に不変ではなく、に制限はありませんset

listOf.set(1, "a");  // UnsupportedOperationException
unmodif.set(1, "a"); // UnsupportedOperationException
asList.set(1, "a");  // modified unmodif! unmodif is not truly unmodifiable

同様に、(保持している場合)バッキング配列を変更すると、リストが変更されます。

構造的不変性には、防御的コーディング、同時実行性、およびセキュリティに関連する多くの副作用が伴いますが、この回答の範囲を超えています。

敵意が無い

List.ofJava 1.5以降のコレクションではnull、要素として使用できません。null要素またはルックアップとして渡そうとすると、になりNullPointerExceptionます。

以来Arrays.asList1.2から収集(コレクション・フレームワーク)で、それができますnull秒。

listOf.contains(null);  // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null);  // allowed

シリアル化されたフォーム

以来List.ofJavaの9に導入され、この方法で作成されたリストは、自分の(バイナリ)直列化形式を持ってきた、彼らは以前のJDKのバージョン(なしに非直列化することはできませんバイナリ互換)。ただし、たとえばJSONを使用して逆シリアル化できます。

身元

Arrays.asList内部的にを呼び出しnew ArrayList、参照の不等式を保証します。

List.of内部実装に依存します。返されるインスタンスは参照の等価性を持つことができますが、これは保証されていないため、それに依存することはできません。

asList1 == asList2; // false
listOf1 == listOf2; // true or false

リストのList.equals作成方法やサポートする操作に関係なく、同じ要素が同じ順序で含まれている場合、リストは(を介して)等しいと言及する価値があります。

asList.equals(listOf); // true i.f.f. same elements in same order

実装(警告:詳細はバージョンによって変わる可能性があります)

リストの要素数List.ofが2以下の場合、要素は特殊な(内部)クラスのフィールドに格納されます。例は、2つの要素(部分的なソース)を格納するリストです。

static final class List2<E> extends AbstractImmutableList<E> {
    private final E e0;
    private final E e1;

    List2(E e0, E e1) {
        this.e0 = Objects.requireNonNull(e0);
        this.e1 = Objects.requireNonNull(e1);
    }
}

それ以外の場合は、と同様の方法で配列に格納されArrays.asListます。

時間とスペースの効率

List.ofフィールドベース(サイズ<2)の実装は、一部の操作でわずかに高速に実行されます。例としてsize()、配列の長さをフェッチせずに定数を返すことができcontains(E e)、反復オーバーヘッドを必要としません。

を介して変更不可能なリストを作成するList.ofことも高速です。上記のコンストラクターを2つの参照割り当て(および要素の任意の量の1つ)と比較します。

Collections.unmodifiableList(Arrays.asList(...));

2つのリストとその他のオーバーヘッドを作成します。スペースの点では、UnmodifiableListラッパーといくつかのペニーを節約できます。結局、HashSet同等の節約はより説得力があります。


結論時間:List.of変更されないリストが必要なArrays.asList場合、および変更可能なリストが必要な場合に使用します(上記を参照)。


1
この答えが存在する理由不思議の人々については、を参照してくださいこれを
user1803551 2017年

3
Arrays.asList完全に変更可能ではありません。asList.add(1);をスローしUnsupportedOperationExceptionます。
Mapeters

「Null敵対的」はそれを置くための素晴らしい方法です。私は、ほとんどの場合List.of、人々が電話をかけたいと思うことがcontainsあり、NullPointerExceptionに驚かされることはありません。
ヌメノン

14

List.ofArrays.asListの違いを要約してみましょう

  1. List.ofデータセットが少なく、変更されていないArrays.asList場合に最適に使用できますが、大規模で動的なデータセットの場合に最適に使用できます。

  2. List.of固定オーバーヘッドの観点からも要素ごとの観点からも、フィールドベースの実装があり、消費するヒープスペースが少ないため、オーバーヘッドスペースが非常に少なくて済みます。一方Arrays.asList、初期化中にヒープ内により多くのオブジェクトが作成されるため、オーバーヘッド領域が増えます。

  3. によって返されるコレクションList.ofは不変であり、したがってスレッドセーフですが、によって返されるコレクションArrays.asListは変更可能であり、スレッドセーフではありません。(変更不可能なコレクションインスタンスは、通常、変更可能なコレクションインスタンスよりもはるかに少ないメモリを消費します。)

  4. List.of許可していないヌルながら要素をArrays.asList可能にヌルの要素を。


2
「変更不可能なコレクションインスタンスは、一般に、変更可能なコレクションインスタンスよりもはるかに少ないメモリを消費します。」- 本当に?それについて少し詳しく説明してもらえますか?それらは安全に共有できるためですか?それとも、インスタンス自体が何らかの方法でより効率的に実装できるということですか?
ハルク、

1
@Hulk回答者はスペース効率について正しい。スチュアートマークスの講演をご覧ください:youtu.be/q6zF3vf114M
t

2
@ZhekaKozlovそれは一般的に真であることが表示されますが、私はについて話すとき、それは本当だと非常に懐疑的だArrays.asListList.of前者は文字通り配列単なるラッパーであることを考えると、。少なくともOpenJDKの実装では、オーバーヘッドが非常に小さいようです。実際、List.of渡された配列のコピーを作成する必要があるため、配列自体がすぐにGCされる予定がない限り、List.ofメモリフットプリントが大幅に大きくなるように見えます。
Chris Hayes、

4
@ChrisHayes少なくともList.of(x)List.of(x, y)、彼らはすべての配列を割り当てていないため、より効率的である
ZhekaKozlov

2
@Hulk:List.ofメソッドが毎回新しいリストを返す必要がないことを忘れないでください。これらのリストには未指定のIDがあるため、JVMレベルでキャッシュ、重複排除、またはスカラー化が処理される可能性があります。このバージョンにない場合は、おそらく次のバージョンにあります。契約で許されています。対照的に、Array.asList結果のリストは配列の可変ビューであり、すべての変化を双方向に反映するため、渡す配列のIDに依存します。
Holger

2

上記の回答とは別に、両方List::ofArrays::asListが異なる特定の操作があります。

+----------------------+---------------+----------+----------------+---------------------+
|      Operations      | SINGLETONLIST | LIST::OF | ARRAYS::ASLIST | JAVA.UTIL.ARRAYLIST |
+----------------------+---------------+----------+----------------+---------------------+
|          add         |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        addAll        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         clear        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        remove        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       removeAll      |       ❗️       |        |        ❗️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       retainAll      |       ❗️       |       |        ❗️        |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|      replaceAll      |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|          set         |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         sort         |       ✔️       |        |        ✔️      |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|  remove on iterator  |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
| set on list-iterator |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
  1. ✔️は、メソッドがサポートされていることを意味します
  2. ❌は、このメソッドを呼び出すとUnsupportedOperationExceptionがスローされることを意味します
  3. ❗️は、メソッドの引数が変更を引き起こさない場合にのみメソッドがサポートされることを意味します。たとえば、Collections.singletonList( "foo")。retainAll( "foo")はOKですが、Collections.singletonList( "foo")。retainAll( "bar" )UnsupportedOperationExceptionをスローします

コレクションの詳細:: singletonList Vs. リスト::の


1
Java試験の回答
povis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.