อะไรคือความแตกต่างระหว่าง 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
ดูคำพูดนี้โดย Stuart "Beaker" Marks
user1803551

20
@ user1803551 แม้ว่าฉันจะเข้าใจถึงความไม่พอใจของคุณ แต่เหตุผลนี้อาจเป็นแบบอย่างที่ไม่ต้องการจริงๆ จำนวนมากของคำถามที่นี่มีคำตอบว่า 'ระบุไว้อย่างชัดเจน' (ขึ้นอยู่กับวิธีการหนึ่งที่กำหนดนี้) ความ ฉันอยากให้คุณนำการสนทนานี้ไปสู่ ​​meta แต่ฉันค่อนข้างมั่นใจว่าการสนทนาดังกล่าวควรมีอยู่แล้ว (และฉันหวังว่าจะมีคนค้นพบและเชื่อมโยง :-)
Dimitris Fasarakis Hilliard

4
@ user1803551 Javadocs ไม่ได้กล่าวถึงความแตกต่างระหว่างรายละเอียดการใช้งานของสองวิธีนี้ (เช่นการใช้พื้นที่หรือประสิทธิภาพ) ฉันคิดว่าผู้คนก็อยากรู้รายละเอียดเหล่านี้เหมือนกัน
ZhekaKozlov

5
@ZhekaKozlov คำตอบที่ได้รับการยอมรับและโหวตมากเกินไปก็ไม่มีเช่นกัน สิ่งนี้บอกอะไรคุณเกี่ยวกับมาตรฐานที่ยอมรับ มันมีข้อมูลน้อยกว่าในเอกสารด้วยซ้ำ (การทำให้เป็นอนุกรมข้อมูลประจำตัวการสั่งซื้อ) หากมีสิ่งใดให้ยื่นคำร้องไปที่ OpenJDK เพื่อเพิ่มข้อมูลนั้น
user1803551

3
คำถามนี้จะถูกกล่าวถึงในเมตา
Dimitris Fasarakis Hilliard

คำตอบ:


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.asListอนุญาตองค์ประกอบว่างในขณะที่List.ofไม่:

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

contains ทำงานแตกต่างกับ nulls:

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> เหมือนตัวอย่างอินเทอร์เฟซจะไม่เพียงพอสำหรับฉันที่จะรู้ว่าฉันจะได้รับข้อยกเว้นรันไทม์หรือไม่หากฉันตรวจสอบ สำหรับ nulls ในทำนองเดียวกันการเปลี่ยนแปลงในการใช้งานวิธีการดังกล่าวอาจส่งผลต่อรหัสที่อยู่ห่างไกลจากไซต์การโทรของวิธีการของฉันหากการตรวจสอบนั้นเกิดขึ้นที่อื่น @Nicolai
Sandy Chapman

8
@SandyChapman นี่อาจเป็นพฤติกรรมที่ไม่คาดคิดสำหรับบางคน (หรือส่วนใหญ่?) แต่เป็นพฤติกรรมที่บันทึกไว้ จากList.contains(Object o)javadoc ของ : "Throws [... ] NullPointerException - หากองค์ประกอบที่ระบุเป็นโมฆะและรายการนี้ไม่อนุญาตให้ใช้องค์ประกอบ null (ทางเลือก)" หรือจากการแนะนำแบบยาวของอินเทอร์เฟซที่มีเพียงไม่กี่คนที่อ่าน: "การใช้งานคอลเล็กชันบางอย่างมีข้อ จำกัด เกี่ยวกับองค์ประกอบที่อาจมี"
Aaron

11
@Aaron อย่างน้อยมันก็เป็นนามธรรมที่รั่วไหลออกมาอย่างดี :)
Sandy Chapman

6
@Sandy แชปแมน: List.of ไม่กลับบางImmutableListชนิดชื่อจริงของมันเป็นเพียงรายละเอียดการดำเนินงานที่ไม่ใช่แบบสาธารณะ ถ้าเป็นสาธารณะและมีคนแคสต์ไปListอีกครั้งความแตกต่างอยู่ตรงไหน? อะไรคือความแตกต่างArrays.asListซึ่งส่งคืนการใช้งานที่ไม่เปิดเผยต่อสาธารณะListที่ทำให้เกิดข้อยกเว้นเมื่อพยายามaddหรือremoveหรือรายการที่ส่งคืนโดยCollections.unmodifiableListที่ไม่อนุญาตให้แก้ไขเลย ทุกอย่างเกี่ยวกับสัญญาที่ระบุไว้ในListอินเทอร์เฟซ Collections อินเทอร์เฟซกับเมธอดเสริมมักไม่บริสุทธิ์ OOP ตั้งแต่ Java 1.2 …
Holger

31

ความแตกต่างระหว่างArrays.asListและList.of

ดูJavaDocsและคำพูดนี้โดย Stuart Marks (หรือเวอร์ชันก่อนหน้า)

ฉันจะใช้สิ่งต่อไปนี้สำหรับตัวอย่างโค้ด:

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

ความไม่เปลี่ยนรูปของโครงสร้าง (หรือ: unmodifiability)

ความพยายามใด ๆ ในการเปลี่ยนแปลงเชิงโครงสร้างList.ofจะส่งผลให้UnsupportedOperationExceptionไฟล์. ซึ่งรวมถึงการดำเนินการเช่นการเพิ่ม , ชุดและลบ อย่างไรก็ตามคุณสามารถเปลี่ยนเนื้อหาของวัตถุในรายการได้ (หากวัตถุไม่เปลี่ยนรูป) ดังนั้นรายการจึงไม่ "ไม่เปลี่ยนรูปโดยสิ้นเชิง"

นี่คือชะตากรรมเดียวกันสำหรับรายการ unmodifiable 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.ofและคอลเลกชันใด ๆ ตั้งแต่ Java 1.5 ไม่อนุญาตให้nullเป็นองค์ประกอบ การพยายามส่งผ่านnullเป็นองค์ประกอบหรือแม้แต่การค้นหาจะส่งผลให้ไฟล์NullPointerException.

เนื่องจากArrays.asListเป็นคอลเล็กชันจาก 1.2 (Collections Framework) จึงอนุญาตให้nulls

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

แบบฟอร์มต่อเนื่อง

เนื่องจากList.ofได้รับการแนะนำใน Java 9 และรายการที่สร้างโดยวิธีนี้มีรูปแบบอนุกรม (ไบนารี) เป็นของตัวเองจึงไม่สามารถ deserialized ใน JDK เวอร์ชันก่อนหน้านี้ได้ (ไม่มีความเข้ากันได้แบบไบนารี ) อย่างไรก็ตามคุณสามารถ de / serialize ด้วย 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.of2 หรือน้อยกว่าองค์ประกอบจะถูกเก็บไว้ในฟิลด์ของคลาสพิเศษ (ภายใน) ตัวอย่างคือรายการที่เก็บ 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 รายการ (และแม้แต่ตัวสร้างสำหรับจำนวนองค์ประกอบโดยพลการ) กับ

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

ซึ่งสร้าง 2 รายการพร้อมค่าใช้จ่ายอื่น ๆ ในแง่ของพื้นที่คุณสามารถประหยัดUnmodifiableListกระดาษห่อหุ้มและเหรียญเพนนี ท้ายที่สุดแล้วการประหยัดที่HashSetเทียบเท่านั้นน่าเชื่อกว่า


เวลาสรุป: ใช้List.ofเมื่อคุณต้องการรายการที่ไม่เปลี่ยนแปลงและArrays.asListเมื่อคุณต้องการรายการที่สามารถเปลี่ยนแปลงได้ (ดังแสดงด้านบน)


1
สำหรับคนที่สงสัยว่าทำไมคำตอบนี้มีอยู่แล้วเห็นนี้
user1803551

3
Arrays.asListไม่เปลี่ยนแปลงอย่างสมบูรณ์ asList.add(1);พ่นUnsupportedOperationExceptionไฟล์.
mapeters

"Null hostile" เป็นวิธีที่ยอดเยี่ยมในการวางไว้ ฉันไม่สามารถใช้List.ofเวลาใด ๆ ที่ผู้คนอาจต้องการโทรcontainsและไม่ต้องแปลกใจกับ NullPointerException
Noumenon

14

ให้สรุปความแตกต่างระหว่างList.ofและArrays.asList

  1. List.ofสามารถใช้งานได้ดีที่สุดเมื่อชุดข้อมูลน้อยลงและไม่มีการเปลี่ยนแปลงในขณะที่Arrays.asListสามารถใช้ได้ดีที่สุดในกรณีของชุดข้อมูลขนาดใหญ่และไดนามิก

  2. List.ofใช้พื้นที่เหนือศีรษะน้อยมากเนื่องจากมีการใช้งานตามภาคสนามและใช้พื้นที่ฮีปน้อยทั้งในแง่ของค่าโสหุ้ยคงที่และแบบต่อองค์ประกอบ ในขณะที่Arrays.asListใช้พื้นที่เหนือศีรษะมากขึ้นเนื่องจากในขณะที่เริ่มต้นจะสร้างวัตถุเพิ่มเติมในฮีป

  3. คอลเล็กชันที่ส่งคืนโดยList.ofไม่เปลี่ยนรูปและด้วยเหตุนี้จึงปลอดภัยต่อเธรดในขณะที่คอลเล็กชันที่ส่งคืนโดยArrays.asListไม่แน่นอนและไม่ปลอดภัยต่อเธรด (โดยทั่วไปแล้วอินสแตนซ์คอลเลคชันที่ไม่เปลี่ยนรูปจะใช้หน่วยความจำน้อยกว่าอินสแตนซ์ที่ไม่สามารถเปลี่ยนแปลงได้)

  4. List.ofไม่อนุญาตองค์ประกอบnullในขณะที่Arrays.asListอนุญาตองค์ประกอบnull


2
"โดยทั่วไปแล้วอินสแตนซ์คอลเลคชันที่ไม่เปลี่ยนรูปจะใช้หน่วยความจำน้อยกว่าอินสแตนซ์ที่เปลี่ยนแปลงไม่ได้" - จริงเหรอ? คุณสนใจที่จะอธิบายรายละเอียดเล็กน้อยหรือไม่ - คุณหมายถึงเพราะสามารถแชร์ได้อย่างปลอดภัยหรือคุณหมายความว่าอินสแตนซ์ของตัวเองสามารถใช้งานได้อย่างมีประสิทธิภาพมากขึ้นหรือไม่?
Hulk

1
@Hulk ผู้ตอบถูกเกี่ยวกับประสิทธิภาพของพื้นที่ ดูการพูดคุยของ Stuart Marks: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov

2
@ZhekaKozlov ดูเหมือนจะเป็นเรื่องจริงโดยทั่วไป แต่ฉันสงสัยมากว่ามันเป็นเรื่องจริงเมื่อพูดถึงArrays.asListเมื่อเทียบกับList.ofเนื่องจากอดีตเป็นเพียงกระดาษห่อหุ้มรอบอาร์เรย์ อย่างน้อยการใช้งาน OpenJDKดูเหมือนว่าจะมีค่าใช้จ่ายที่น้อยมาก ในความเป็นจริงList.ofจะต้องทำสำเนาของอาร์เรย์ใด ๆ ที่ส่งผ่านดังนั้นเว้นแต่ว่าอาร์เรย์จะเป็น GC ในเร็ว ๆ นี้ดูเหมือนว่าจะList.ofมีขนาดหน่วยความจำที่ใหญ่กว่ามาก
Chris Hayes

4
@ChrisHayes อย่างน้อยList.of(x)และList.of(x, y)มีประสิทธิภาพมากขึ้นเพราะไม่จัดสรรอาร์เรย์เลย
ZhekaKozlov

2
@Hulk: อย่าลืมว่าList.ofวิธีนี้ไม่จำเป็นต้องส่งคืนรายการใหม่ทุกครั้ง รายการเหล่านี้มีข้อมูลประจำตัวที่ไม่ระบุดังนั้นจึงอาจมีการจัดการแคชหรือการคัดลอกข้อมูลซ้ำซ้อนหรือการปรับสเกลาร์ในระดับ JVM หากไม่ได้อยู่ในเวอร์ชันนี้อาจเป็นในครั้งต่อไป ได้รับอนุญาตตามสัญญา ในทางตรงกันข้ามArray.asListขึ้นอยู่กับข้อมูลประจำตัวของอาร์เรย์ที่คุณส่งผ่านเนื่องจากรายการผลลัพธ์เป็นมุมมองที่ไม่แน่นอนบนอาร์เรย์ซึ่งสะท้อนถึงการเปลี่ยนแปลงทั้งหมดแบบสองทิศทาง
Holger

3

นอกเหนือจากคำตอบข้างต้นมีการดำเนินการบางอย่างที่ทั้งสองList::ofและArrays::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") retentionAll ("foo") ใช้ได้ แต่ Collections.singletonList ("foo") retentionAll ("bar" ) พ่น UnsupportedOperationException

เพิ่มเติมเกี่ยวกับคอลเลกชัน :: singletonList Vs. รายการของ


1
คำตอบสำหรับการสอบ java
povis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.