บทความนี้จะดูรายละเอียดเกี่ยวกับคลาส ArrayList จาก Collections Framework ซึ่งอาจเป็นวิธีที่ง่ายที่สุดที่จะเข้าใจ เนื่องจากคลาสนี้ใช้อาร์เรย์ปกติ คุณเกือบจะถูกถามคำถามเกี่ยวกับคลาสนี้และการนำไปใช้งานใน Java ในการสัมภาษณ์อย่างแน่นอน ในส่วนที่สอง เราจะวิเคราะห์วิธีการที่เหลือและเขียนการใช้งานอาร์เรย์แบบไดนามิกสำหรับตัวเลขของเราเอง คลาส ArrayList สืบทอดมาจากคลาส AbstractList และใช้อินเทอร์เฟซต่อไปนี้: List, RandomAccess, Cloneable, Serializable การวิเคราะห์โดยละเอียดของคลาส ArrayList [ตอนที่ 2] คลาส ArrayList รองรับอาร์เรย์แบบไดนามิกที่สามารถขยายได้ตามต้องการ ความจำเป็นและประสิทธิผลของอาร์เรย์อธิบายได้จากข้อเท็จจริงที่ว่าอาร์เรย์ปกติมีความยาวคงที่ เมื่อสร้างขึ้นแล้ว จะไม่สามารถขยายหรือลดขนาดได้ ซึ่งจะกำหนดข้อจำกัดหากไม่ทราบว่าจำเป็นต้องใช้อาร์เรย์ขนาดใหญ่เพียงใด โดยพื้นฐานแล้วคลาส ArrayList คืออาร์เรย์รายการที่มีความยาวผันแปรได้ของการอ้างอิงอ็อบเจ็กต์ สิ่งสำคัญคือต้องเข้าใจว่าขนาด (จำนวนเซลล์) ของอาร์เรย์ภายในจะไม่ลดลงโดยอัตโนมัติเมื่อองค์ประกอบถูกลบออกจากอาร์เรย์ ในความเป็นจริง ค่าของตัวแปร
ดังนั้นเมื่อองค์ประกอบถูกแทรกที่ดัชนีและไม่มีพื้นที่ว่างในอาร์เรย์ การเรียก
คุณยังสามารถเพิ่มองค์ประกอบต่างๆ ให้กับคอลเลกชันของเราได้โดยใช้นามสกุลไฟล์
ลองลบองค์ประกอบที่ดัชนี 3 ออกจากอาร์เรย์ด้านล่าง:
ลองพิจารณาวิธีการรุ่นที่สอง:
ลบองค์ประกอบที่ไม่ได้อยู่ในคอลเลกชันที่ผ่าน:
สมมติว่าเรามีคอลเลกชัน:
การแทนที่องค์ประกอบ
ตรวจสอบคอลเลกชันสำหรับองค์ประกอบ:
size
ซึ่งระบุจำนวนองค์ประกอบที่มีอยู่ในอาร์เรย์นั้นลดลง สมมติว่าเราสร้างอ็อบเจ็กต์ใหม่ของคลาส ArrayList และเพิ่มองค์ประกอบ 5 รายการลงไป ตามค่าเริ่มต้น อาร์เรย์ที่มี 10 องค์ประกอบจะถูกสร้างขึ้น ในกรณีนี้ สิ่งที่เรียกว่าความจุ (ขนาด/ปริมาตร) ของออบเจ็กต์ของเราจะเท่ากับ 10 แต่ค่าของตัวแปรsize
จะเท่ากับ 5 และเมื่อเราลบองค์ประกอบ เราจะเห็นการเปลี่ยนแปลงในค่าของตัวแปรsize
เนื่องจากเรา.length
ไม่สามารถเข้าถึงอาร์เรย์ภายในของคลาส ArrayList และค้นหาความยาวของมันได้ สามารถลดขนาดได้โดยใช้วิธีการเพิ่มเติมtrimToSize()
ซึ่งจะกล่าวถึงด้านล่าง มาดูที่สนามของชั้นเรียนกัน
-
ฟิลด์ที่รับผิดชอบปริมาณเริ่มต้นของอาร์เรย์ไดนามิก:
private static final int DEFAULT_CAPACITY = 10
เมื่อสร้างวัตถุใหม่ new ArrayList<>() (ตัวสร้างที่ไม่มีพารามิเตอร์) จะมีการสร้างอาร์เรย์ 10 องค์ประกอบภายใน
-
ฟิลด์ที่องค์ประกอบทั้งหมดของคอลเลกชันจะถูกจัดเก็บ:
transient Object[] elementData
ทำเครื่องหมายด้วยคำสำคัญ
transient
- ฟิลด์จะไม่ถูกเขียนลงในสตรีมไบต์เมื่อใช้อัลกอริธึมการทำให้เป็นอนุกรมมาตรฐาน เป็นที่น่าสังเกตว่าฟิลด์นี้ไม่ได้ทำเครื่องหมายด้วยคำหลักprivate
แต่สิ่งนี้เสร็จสิ้นเพื่ออำนวยความสะดวกในการเข้าถึงฟิลด์นี้จากคลาสที่ซ้อนกัน (เช่น SubList) - ฟิลด์ตัวนับที่เก็บจำนวนองค์ประกอบจริงในอาร์เรย์:
private int size
ค่าจะเพิ่มขึ้น/ลดลงเมื่อดำเนินการต่างๆ เช่น การแทรกและการลบ
public ArrayList()
– สร้างอาร์เรย์รายการว่าง 10 องค์ประกอบpublic ArrayList(Collection < ? extends E > c)
– สร้างอาร์เรย์รายการเริ่มต้นด้วยองค์ประกอบจากคอลเลกชันที่ส่งผ่าน (หากเราต้องการสร้าง ArrayList ใหม่ตามคอลเลกชันบางส่วน)public ArrayList(int initialCapacity)
– สร้างอาร์เรย์รายการที่มีความจุเริ่มต้น หากพารามิเตอร์ที่ส่งผ่าน defaultCapacity มากกว่า 0 แสดงว่าอาร์เรย์ของขนาดที่ระบุจะถูกสร้างขึ้น (ฟิลด์ภายใน elementData ได้รับการกำหนดลิงก์ไปยังอาร์เรย์ประเภท Object ใหม่ที่มีขนาด defaultCapacity) หากพารามิเตอร์เป็น 0 แสดงว่าอาร์เรย์ว่างจะถูกสร้างขึ้น หากพารามิเตอร์ที่ระบุน้อยกว่า 0 แสดงว่า IllegalArgumentException ถูกส่งออกไป
List < String> list = new ArrayList<>();
วัตถุที่สร้างขึ้นใหม่มีlist
คุณสมบัติ (เขตข้อมูล) elementData
และ size
ที่เก็บค่านั้นelementData
ไม่มีอะไรมากไปกว่าอาร์เรย์ประเภทเฉพาะ (ระบุในรูปแบบทั่วไป – <>
) ในกรณีของString[]
เรา หากเรียกใช้คอนสตรัคเตอร์ที่ไม่มีพารามิเตอร์ ตามค่าเริ่มต้นอาร์เรย์ขององค์ประกอบ 10 ประเภท Object จะถูกสร้างขึ้น (แน่นอนว่าต้องมีการส่งประเภท) การเพิ่มองค์ประกอบ การเพิ่มองค์ประกอบแบบคลาสสิกให้กับอาร์เรย์รายการทำได้โดยใช้ตัวแปรที่โอเวอร์โหลดของadd()
.
public boolean add(E элемент)
เรามาเพิ่มกันดีกว่า: list.add("0");
ภายในวิธีนี้ มีการเรียกเวอร์ชันที่โอเวอร์โหลดของเมธอดadd()
โดยทำเครื่องหมายเป็นprivate
ซึ่งจะรับพารามิเตอร์สามตัวเป็นอินพุต: องค์ประกอบที่จะเพิ่ม อาร์เรย์ภายใน และขนาดของมัน ในวิธีการส่วนตัว การตรวจสอบจะเกิดขึ้น: หากพารามิเตอร์ขนาดที่ส่งผ่านเท่ากับความยาวของอาร์เรย์ภายใน (นั่นคืออาร์เรย์เต็ม) จากนั้นอาร์เรย์จะถูกกำหนดผลลัพธ์ของวิธีการgrow(int minCapacity)
(ค่าปัจจุบันของฟิลด์ ขนาด + 1 ถูกส่งไปยังวิธีการเนื่องจากจำเป็นต้องคำนึงถึงองค์ประกอบที่ถูกเพิ่ม) ซึ่งอาร์เรย์ภายในถูกกำหนดลิงก์ไปยังอาร์เรย์ที่สร้างขึ้นใหม่ที่ได้รับโดยการคัดลอกองค์ประกอบของอาร์เรย์ดั้งเดิม:
Arrays.copyOf(elementData, newCapacity(minCapacity))
เนื่องจากเป็นพารามิเตอร์ตัวที่สองของเมธอดcopyOf
เราจะระบุผลลัพธ์ของเมธอดnewCapacity(int minCapacity)
ซึ่งมีการคำนวณขนาดอาร์เรย์ใหม่ภายใน คำนวณโดยใช้สูตรต่อไปนี้: int newCapacity = oldCapacity + (oldCapacity >> 1)
สำหรับอาร์เรย์ที่มีขนาดเริ่มต้น ค่าต่อไปนี้จะเป็นจริง: >> 1
– เลื่อนบิตไปทางขวาทีละหนึ่ง (ตัวดำเนินการที่ลดจำนวนลงครึ่งหนึ่ง) โดยพื้นฐานแล้วมันหมายถึงการหารด้วย 2 ยกกำลัง 1 ปรากฎว่าเราหาร 10 ด้วย 2 แล้วบวก 10 รวมแล้ว ความจุใหม่ของอาเรย์คือ 15 แต่เนื่องจากเราเพิ่มองค์ประกอบที่ 11 จึงเป็น 15 + 1 = 16. กลับไปที่รายการของเราและสมมติว่าเราได้เพิ่มองค์ประกอบไปแล้ว 10 รายการแล้วลองเพิ่ม 11 รายการ การตรวจสอบจะแสดงว่าไม่มีช่องว่างในอาร์เรย์ ดังนั้นอาร์เรย์ใหม่จะถูกสร้างขึ้นและเรียกว่าArrays.copyOf
ซึ่งใช้เมธอดของระบบเป็นการSystem.arraycopy()
ภายใน หรือนี่คือตัวอย่างที่ชัดเจนจากบทความหนึ่งเกี่ยวกับ JavaRush: หลังจากตรวจสอบทั้งหมดนี้และเพิ่มขนาดของอาร์เรย์หากจำเป็น จากนั้นในวิธีส่วนตัวadd()
องค์ประกอบใหม่จะถูกเพิ่มที่ส่วนท้ายของอาร์เรย์ และพารามิเตอร์ปัจจุบันsize
จะเพิ่มขึ้นหนึ่งรายการ . อาร์เรย์เก่าจะถูกประมวลผลโดยตัวรวบรวมขยะในภายหลัง นี่คือวิธีการทำงานของอาร์เรย์แบบไดนามิก: เมื่อเราเพิ่มองค์ประกอบ เราจะตรวจสอบว่ายังมีที่ว่างในนั้นหรือไม่ หากมีช่องว่าง เราก็เพียงเพิ่มองค์ประกอบที่ส่วนท้ายของอาร์เรย์ จุดสิ้นสุดไม่ได้หมายถึงเซลล์สุดท้ายในอาร์เรย์ แต่เป็นเซลล์ที่สอดคล้องกับsize
ค่า เราได้เพิ่มองค์ประกอบแรกลงในอาร์เรย์ โดยวางไว้ในเซลล์ที่มีดัชนี [0] ค่าฟิลด์size
เพิ่มขึ้นหนึ่งและ = 1 เราเพิ่มองค์ประกอบถัดไป: เราเห็นว่าsize = 1
ดังนั้นเราจึงวางองค์ประกอบในเซลล์ที่มีดัชนี [1] และอื่น ๆ มีเมธอดเวอร์ชันโอเวอร์โหลดที่มีพารามิเตอร์สองตัว:
public void add(int index, E element)
เราสามารถระบุตำแหน่ง (ดัชนี) ของเซลล์ที่เราต้องการเพิ่มองค์ประกอบได้ ขั้นแรก ตรวจสอบความถูกต้องของค่าดัชนีที่ระบุ เนื่องจากมีความเป็นไปได้ที่ดัชนีที่ไม่ถูกต้องจะถูกระบุ ซึ่งจะชี้ไปที่เซลล์ที่ไม่มีอะไรเลย หรือไม่มีอยู่เลย การตรวจสอบดัชนี: index > size || index < 0
– หากดัชนีที่ระบุมีขนาดใหญ่กว่าขนาดปัจจุบันของอาร์เรย์หรือน้อยกว่า 0 ข้อยกเว้นจะถูกส่งออกIndexOutOfBoundsException
ไป จากนั้น หากจำเป็น ขนาดของอาร์เรย์จะเพิ่มขึ้น คล้ายกับตัวอย่างด้านบน คุณคงเคยได้ยินมาว่าในระหว่างการเพิ่ม/ลบการดำเนินการในอาร์เรย์ มีบางอย่างถูกเลื่อนไปที่ไหนสักแห่ง (ไปทางขวาหรือทางซ้าย) ดังนั้น การเปลี่ยนแปลงจะดำเนินการโดยการคัดลอกอาร์เรย์: System.arraycopy(elementData, index, elementData, index + 1, s - index);
องค์ประกอบทั้งหมดที่อยู่ทางด้านขวาของดัชนีที่ระบุจะถูกเลื่อนไปทางขวาหนึ่งตำแหน่ง (ดัชนี +1) และหลังจากนั้นองค์ประกอบใหม่จะถูกเพิ่มลงในอาร์เรย์ภายในตามดัชนีที่ระบุ เนื่องจากเราได้เลื่อนส่วนหนึ่งของอาร์เรย์ไปทางขวาทีละหนึ่ง (ไม่ได้สร้างอาร์เรย์ใหม่) เซลล์ที่เราต้องการจึงมีอิสระในการเขียน ลิงก์ไปยังอาเรย์เก่าจะถูกลบและในอนาคตจะถูกยึดครองโดยตัวรวบรวมขยะ วาง "maserati" ลงในเซลล์ [3] ซึ่งถูกครอบครองแล้ว:
System.arraycopy()
จะเกิดขึ้นสองครั้ง: ครั้งแรกใน ครั้งgrow()
ที่สองในวิธีการนั้นเองadd(index, value)
ซึ่งจะส่งผลต่อความเร็วของการดำเนินการเพิ่มทั้งหมดอย่างชัดเจน เป็นผลให้เมื่อจำเป็นต้องเขียนองค์ประกอบอื่นลงในอาร์เรย์ภายใน แต่ไม่มีที่ว่างดังนั้นนี่คือสิ่งที่เกิดขึ้นภายใน ArrayList:
- อาร์เรย์ใหม่จะถูกสร้างขึ้นโดยมีขนาดใหญ่กว่าอาร์เรย์เดิมถึง 1.5 เท่า บวกด้วยองค์ประกอบหนึ่งรายการ
- องค์ประกอบทั้งหมดจากอาร์เรย์เก่าจะถูกคัดลอกไปยังอาร์เรย์ใหม่
- อาร์เรย์ใหม่จะถูกเก็บไว้ในตัวแปรภายในของวัตถุ ArrayList และอาร์เรย์เก่าจะถูกประกาศเป็นขยะ
public void ensureCapacity(int minCapacity)
ด้วยการเพิ่มความจุอาเรย์ล่วงหน้า คุณสามารถหลีกเลี่ยงการแจกจ่าย RAM เพิ่มเติมในภายหลังได้ วิธีการนี้จะเพิ่มขนาดของอาร์เรย์ภายในเพื่อรองรับจำนวนองค์ประกอบที่ส่งผ่านไปminCapacity
ยัง วิธีการนี้ensureCapacity()
ไม่ส่งผลกระทบต่อฟิลด์size
แต่จะส่งผลต่อcapacity
(ขนาดของ) อาร์เรย์ภายใน ฉันขอย้ำอีกครั้งว่าsize
ทั้งสองcapacity
สิ่งต่างกันและมันสำคัญมากที่จะไม่สับสน! หากคุณต้องการลดขนาดของอาร์เรย์พื้นฐานที่ใช้สร้าง ArrayList ให้เป็นจำนวนองค์ประกอบปัจจุบันที่เก็บไว้จริง คุณควรเรียกtrimToSize()
. หลังจากลบองค์ประกอบออกจากคอลเลกชันแล้วsize()
มันจะแสดงจำนวนองค์ประกอบที่มีอยู่จริง และcapacity
จะไม่ลดลง! สมมติว่า: เราป้อน 100 องค์ประกอบลบ 50 รายการแรกsize
มันจะเท่ากับ 50 และจะcapacity
ยังคงอยู่ 100 เพื่อลด และcapacity
เราจำเป็นต้องใช้วิธีการtrimToSize()
ซึ่งปรับความจุทั้งหมดของเราให้เป็นขนาดปัจจุบัน มันพอดียังไงบ้าง? คัดลอกอาร์เรย์ของเราเพื่อไม่ให้มีเซลล์ว่างเหลืออยู่ (ความยาวของอาร์เรย์ใหม่จะเท่ากับฟิลด์ขนาด)
addAll
.
public boolean addAll(Collection< ? extends E> c)
public boolean addAll(int index, Collection< ? extends E> collection);
ตัวเลือกแรกช่วยให้คุณสามารถเพิ่มองค์ประกอบทั้งหมดจากคอลเลกชันที่ระบุในพารามิเตอร์วิธีการ (เช่น แผ่นงานอื่น) ไปยังคอลเลกชันดั้งเดิม (แทรกที่ส่วนท้าย) ที่มีการเรียกใช้เมธอด คอลเลกชันที่ส่งผ่าน (อาจเป็นชุดก็ได้) จะถูกแปลงเป็นอาร์เรย์โดยใช้นามสกุลtoArray()
. โดยปกติแล้ว การดำเนินการเพิ่มจะดำเนินการโดยใช้การคัดลอกด้วย ประการที่สองคือการเพิ่มองค์ประกอบทั้งหมดcollection
ลงในรายการโดยเริ่มจากindex
ดัชนี ในกรณีนี้ องค์ประกอบทั้งหมดจะเลื่อนไปทางขวาตามจำนวนองค์ประกอบในcollection
รายการ การลบองค์ประกอบ ขั้นแรก มาดูตัวเลือกคลาสสิกสำหรับการลบองค์ประกอบออกจาก ArrayList
public E remove(int index)
ดำเนินการลบตามดัชนีและเลื่อนองค์ประกอบที่ตามมาทั้งหมด (หลังจากองค์ประกอบที่ดัชนีที่ระบุ) ไปทางซ้าย ดังนั้นจึงเป็นการปิด "รู" นอกจากนี้ยังส่งคืนองค์ประกอบที่ถูกลบ (E) ซึ่งก่อนหน้านี้ถูกเขียนไปยังตัวแปรเพิ่มเติมก่อนการลบ ซึ่งเป็นค่าที่เราได้รับจากการเรียกเมธอด เพื่อทำความเข้าใจว่า E คืออะไร คุณจะต้องทำความคุ้นเคยกับสิ่งที่เรียกว่าประเภททั่วไป สัญกรณ์ E บ่งชี้ว่าวิธีการส่งคืนประเภทข้อมูลที่ระบุไว้เมื่อสร้างวัตถุ ArrayList (โปรดจำไว้ว่า: List <String> list
ดังนั้นในกรณีนี้ E จะถูก “ทดแทน” String
) เพื่อความเข้าใจโดยทั่วไป ฉันขอแนะนำอย่างยิ่งให้คุณทำความคุ้นเคยกับประเภททั่วไป มีการตรวจสอบความถูกต้องของดัชนีที่ป้อน จากนั้นภายในเมธอดองค์ประกอบจะไม่ถูกลบทั้งหมด แต่จะเรียกว่าเมธอดส่วนตัวfastRemove(Object[] es, int i)
ซึ่งการลบเกิดขึ้นแล้ว เราส่งผ่านอาร์เรย์ของเราและดัชนีที่ระบุไปยังวิธีการเป็นอินพุต องค์ประกอบจะถูกคัดลอกโดยใช้System.arraycopy()
ขนาดของอาร์เรย์จะลดลง จากนั้นเราจะกำหนดค่าว่างให้กับองค์ประกอบสุดท้าย เป็นที่น่าสังเกตว่าไม่มีการสร้างอาร์เรย์ใหม่: System.arraycopy(es, i + 1, es, i, size - 1 - i);
ส่วนที่อยู่ทางด้านขวาของตำแหน่งภายใต้ดัชนีที่ระบุ (i+1) จะถูกคัดลอกไปยังอาร์เรย์ดั้งเดิมของเรา (es) และเริ่มต้นจากตำแหน่งนั้น (i) ตำแหน่งที่องค์ประกอบนั้นถูกลบออก ดังนั้นเราจึงทำการเลื่อนไปทางซ้ายและลบองค์ประกอบของเรา
public boolean remove(Object o)
วิธีการลบองค์ประกอบที่ส่งผ่านออกจากรายการo
หรือแม่นยำยิ่งขึ้นคือวัตถุที่ลิงก์ที่ระบุ หากมีองค์ประกอบอยู่ในรายการ องค์ประกอบนั้นจะถูกลบออกและองค์ประกอบทั้งหมดจะเลื่อนไปทางซ้าย หากมีองค์ประกอบอยู่ในรายการและถูกลบออกสำเร็จ เมธอดจะคืนค่าเป็นจริง มิฉะนั้นจะเป็นเท็จ คล้ายกับตัวเลือกที่มีการลบตามดัชนี วิธีการนี้เรียกว่าfastRemove()
โดยที่การกระทำเดียวกันจะเกิดขึ้นทุกประการ ข้อแตกต่างก็คือวิธีการremove(Object o)
ค้นหาวัตถุที่ต้องการเพิ่มเติมผ่านวิธีการequals()
ของคลาส Object เมื่อลบตามค่า การวนซ้ำจะผ่านองค์ประกอบทั้งหมดของรายการจนกว่าจะพบรายการที่ตรงกัน เฉพาะองค์ประกอบแรกที่พบเท่านั้นที่จะถูกลบ สรุป: เมื่อลบองค์ประกอบออกจากอาร์เรย์แบบไดนามิก จะไม่เหลือรูเหมือนในอาร์เรย์ปกติ (เซลล์ที่ถูกลบจะไม่ว่างเปล่า) องค์ประกอบที่ตามมาทั้งหมด (ซึ่งอยู่ทางด้านขวาของดัชนี) จะถูกเลื่อนไปทางซ้ายหนึ่งตำแหน่ง มีวิธีการเพิ่มเติมหลายวิธีที่สามารถใช้เพื่อลบองค์ประกอบออกจากรายการตามระดับที่แตกต่างกัน ลองดูพวกเขาสั้น ๆ ทำความสะอาดคอลเลกชันของเรา:
public void clear()
การวนซ้ำอย่างง่ายfor
จะวนซ้ำองค์ประกอบทั้งหมดของอาร์เรย์ โดยกำหนดค่าว่างให้กับแต่ละองค์ประกอบ คุณสามารถลบองค์ประกอบเหล่านั้นออกจากคอลเลกชันของเราที่มีอยู่ในคอลเลกชันที่ถ่ายโอนอื่นได้ดังนี้:
public boolean removeAll(Collection< ?> c)
หากคุณต้องการลบองค์ประกอบหลายรายการ คุณไม่ควรทำในลักษณะวนซ้ำแบบมีเงื่อนไข: สะดวกกว่าและปลอดภัยกว่าในการใช้วิธีremoveAll()
นี้ ยอมรับชุดองค์ประกอบที่จะถูกลบออกจากรายการ คอลเลกชันจะต้องมีองค์ประกอบประเภทเดียวกับที่รายการเป้าหมายเก็บไว้ ไม่เช่นนั้นจะถูกโยนทิ้งClassCastException
ไป เมธอดจะคืนค่าเป็นจริงหากรายการมีการเปลี่ยนแปลงอันเป็นผลมาจากการเรียกเมธอด
public boolean retainAll(Collection< ?> c)
List< String> listFirst = new ArrayList<>();
listFirst.add("White");
listFirst.add("Black");
listFirst.add("Red");
และประการที่สอง:
List< String> listSecond = new ArrayList<>();
listSecond.add("Green");
listSecond.add("Red");
listSecond.add("White");
จากนั้นหลังจากนั้นlistSecond.retainAll(listFirst)
จะlistSecond
ยังคงอยู่:
"White"
"Red"
เนื่องจาก "สีเขียว" ถูกลบออกซึ่งไม่ได้อยู่ในlistFirst
. แต่หลังจากlistSecond.removeAll(listFirst)
นั้นก็listSecond
จะยังคงอยู่:
"Green"
Удалorсь все элементы, которые есть в listFirst.
ไม่เป็นของคอลเลกชันที่ส่งผ่าน - หมายความว่าหากมีองค์ประกอบที่ไม่ได้อยู่ในคอลเลกชันที่ส่งผ่าน คุณจะต้องลบองค์ประกอบเหล่านั้นออกจากองค์ประกอบแรก (ซึ่งใช้วิธีการนั้น) เป็นของคอลเลกชันที่ถ่ายโอน - ดังนั้น หากมีองค์ประกอบในคอลเลกชันที่หนึ่งและที่สอง (ถ่ายโอน) รายการที่ซ้ำกันจากคอลเลกชันแรกจะถูกทำลาย
protected void removeRange(int fromIndex, int toIndex)
ลบองค์ประกอบทั้งหมดที่อยู่ระหว่างดัชนีเริ่มต้นที่ระบุ (รวม) และดัชนีที่ระบุสิ้นสุด (ไม่รวม) ออกจากรายการ เป็นที่น่าสังเกตว่าไม่สามารถเรียกวิธีการนี้บนวัตถุ ArrayList ได้โดยตรง หากต้องการใช้คุณจะต้องสืบทอดจากAbstractList/ArrayList
. วิธีการนี้ยังใช้โดยวิธีอื่น (รายการย่อยซึ่งจะกล่าวถึงในภายหลัง)
public boolean removeIf(Predicate< ? super E> filter)
ลบองค์ประกอบออกจากคอลเลกชันตามภาคแสดงที่กำหนด ภาคแสดงนั้นเป็นฟังก์ชัน/อัลกอริธึม/เงื่อนไขบางอย่าง ซึ่งองค์ประกอบหนึ่งหรือหลายองค์ประกอบที่สอดคล้องกับเงื่อนไขที่กำหนดจะถูกลบออก Predicate
— อินเทอร์เฟซการทำงาน (มีเพียงวิธีเดียวดังนั้นจึงสามารถใช้เป็นแลมบ์ดาได้) ทำงานบนหลักการ “ได้รับพารามิเตอร์เดียว - ส่งคืนบูลีน” โดยพื้นฐานแล้ว วิธีการนี้จะแทนที่การใช้งานจากอินเทอร์เฟซCollection
และใช้ "กลยุทธ์" ต่อไปนี้ โดยจะวนซ้ำองค์ประกอบต่างๆ และทำเครื่องหมายองค์ประกอบที่ตรงกับPredicate
; จากนั้นจะดำเนินการเป็นครั้งที่สองเพื่อลบ (และเปลี่ยน) องค์ประกอบที่ถูกทำเครื่องหมายในการวนซ้ำครั้งแรก ลองใช้อินเทอร์เฟซPredicate
ที่จะคืนค่าเป็นจริงหากวัตถุทั้งสองเท่ากัน:
class SamplePredicate< T> implements Predicate< T>{
T varc1;
public boolean test(T varc){
if(varc1.equals(varc)){
return true;
}
return false;
}
}
ในคลาสอื่น เรามาสร้าง ArrayList จากString
และอ็อบเจ็กต์ของคลาสของเราที่ใช้Predicate
:
ArrayList< String> color_list = new ArrayList<> ();
SamplePredicate< String> filter = new SamplePredicate<> ();
varc1
มาเขียน ค่า "White" ให้กับตัวแปร :
filter.varc1 = "White";
มาเพิ่มสองสามบรรทัดในรายการ:
color_list.add("White");
color_list.add("Black");
color_list.add("Red");
color_list.add("White");
color_list.add("Yellow");
color_list.add("White");
เรามาดำเนินการตามวิธีการในรายการremoveIf
ซึ่งเราจะส่งอ็อบเจ็กต์ของเราไปโดยมีเงื่อนไข:
color_list.removeIf(filter);
ด้วยเหตุนี้ แถวทั้งหมดที่มีค่า "สีขาว" จะถูกลบออกจากรายการ เนื่องจาก "ภาคแสดง" ของเราเปรียบเทียบแถวเหล่านั้นเพื่อความเท่าเทียมกัน รายการสุดท้าย: [ดำ แดง เหลือง]
public E set(int index, E element)
แทนที่องค์ประกอบในตำแหน่งที่ระบุindex
ด้วยองค์ประกอบที่ส่งelement
ผ่าน ดัชนีจะต้องมากกว่าศูนย์และน้อยกว่าดัชนีขององค์ประกอบสุดท้าย มิฉะนั้นจะเกิดข้อIndexOutOfBoundsException
ยกเว้น ไม่มีสำเนาของอาร์เรย์ภายในเกิดขึ้น เพียงแทนที่องค์ประกอบตามดัชนีที่ระบุ องค์ประกอบใหม่จะถูกแทรกเข้าไป เช่น เขียนทับค่า
public void replaceAll(UnaryOperator<e> operator)
เปลี่ยนแปลงองค์ประกอบทั้งหมดของคอลเลกชัน (เป็นไปได้โดยมีเงื่อนไข) ส่วนใหญ่ใช้ร่วมกับlambdasหรือคลาสที่ไม่ระบุชื่อ (แต่เพื่อความชัดเจน ในตัวอย่างเราจะใช้คลาสที่ใช้อินเทอร์เฟซ) ที่ใช้อินเทอร์เฟซUnaryOperator
และกำหนดวิธีการของมัน ลองใช้อินเทอร์เฟซ:
class MyOperator< T> implements UnaryOperator< T>{
T varc1;
public T apply(T varc){
return varc1;
}
}
ในคลาสอื่น เรามาสร้าง ArrayList จากString
และอ็อบเจ็กต์ของคลาสของเราที่ใช้UnaryOperator
:
ArrayList< String> color_list = new ArrayList<> ();
MyOperator< String> operator = new MyOperator<> ();
varc1
มาเขียน ค่า "White" ให้กับตัวแปร :
operator.varc1 = "White";
มาเพิ่มสองสามบรรทัดในรายการ:
color_list.add("White");
color_list.add("Black");
color_list.add("Red");
color_list.add("White");
color_list.add("Yellow");
color_list.add("White");
ลองใช้วิธีการในรายการreplaceAll
ที่เราจะส่งวัตถุของเราไปoperator
:
color_list.replaceAll(operator);
เป็นผลให้ค่าทั้งหมดในรายการถูกแทนที่ด้วย "สีขาว": [ขาว, ขาว, ขาว, ขาว, ขาว, ขาว] และนี่คือวิธีการลบช่องว่างทั้งหมดออกจากสตริงที่อยู่ในคอลเลกชัน:
ArrayList< String> list = new ArrayList<>(Arrays.asList("A ", " B ", "C"));
list.replaceAll(String::trim);
วิธีการอื่น: คุณสามารถแปลงอาร์เรย์รายการ ArrayList เป็นอาร์เรย์ปกติได้โดยใช้วิธีการ:
public Object[] toArray()
หรือ
public < T> T[] toArray(T[] a)
- ที่นี่กำหนดประเภทของอาร์เรย์ที่ส่งคืนในruntime
วิธีนี้จะช่วยให้:
- เร่งการดำเนินการบางอย่าง
- ส่งผ่านอาร์เรย์เป็นพารามิเตอร์ไปยังวิธีการที่ไม่โอเวอร์โหลดเพื่อยอมรับการรวบรวมโดยตรง
- การรวมรหัสตามคอลเลกชันใหม่เข้ากับรหัสเดิมที่ไม่รู้จักคอลเลกชัน
public Object clone()
โปรดทราบว่าเมธอดนี้clone()
ส่งคืนประเภท Object ดังนั้นหลังจากเรียกมันแล้ว คุณจะต้องส่งไปยังคลาสที่ต้องการ การโคลนนิ่งจะสร้างวัตถุอิสระใหม่ ตรวจสอบคอลเลกชันว่ามีวัตถุอยู่หรือไม่:
public boolean contains(Object o)
ตรวจสอบการมีอยู่ของวัตถุในรายการ (ภายในโดยใช้เมธอดเท่ากับของคลาส Object เช่น เปรียบเทียบการอ้างอิง) คืนค่าจริง/เท็จ ขึ้นอยู่กับผลลัพธ์ นอกเหนือจากการวนซ้ำตามปกติ คุณสามารถวนซ้ำ (เข้าถึงแต่ละองค์ประกอบ รวมถึงดำเนินการบางอย่าง) คอลเลกชันโดยใช้:
public void forEach(Consumer< ? super E> action)
นี่คือวิธีที่เราสามารถแสดงรายการของเรา:
List< Integer> numbers = new ArrayList<>(Arrays.asList(10, 20, 50, 100, -5));
numbers.forEach((number)-> System.out.println(number));
โดยไม่ต้องใช้ lambdas คุณต้องใช้คลาสที่ไม่ระบุชื่อและแทนที่ วิธี accept
อินเทอร์เฟซConsumer
:
numbers.forEach(new Consumer< Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
รับองค์ประกอบตามดัชนี:
public E get(int index)
ใช้สำหรับการเข้าถึงองค์ประกอบคอลเลกชันแบบสุ่ม ส่งคืนองค์ประกอบที่อยู่ในรายการที่ดัชนีที่ระบุ ถ้าindex < 0
หรือ เป็นindex >=
จำนวนองค์ประกอบสูงสุดในรายการ ข้อยกเว้นจะถูกส่งออกIndexOutOfBoundsException
ไป นี่เป็นวิธีการพื้นฐานในการดึงองค์ประกอบจากรายการ และเวลาในการดึงองค์ประกอบตามดัชนีจะเท่ากันเสมอ ไม่ว่า ArrayList จะมีขนาดเท่าใดก็ตาม เนื่องจากกำลังเข้าถึงเซลล์อาร์เรย์ที่ระบุ ค้นหาดัชนีสำหรับวัตถุที่ระบุ:
public int indexOf(Object o);
public int lastIndexOf(Object o);
วิธีการส่งคืนดัชนีขององค์ประกอบแรก (เมื่อพบวัตถุที่กำหนดครั้งแรก) หรือเกิดขึ้นครั้งล่าสุด (เมื่อพบวัตถุที่กำหนดครั้งล่าสุด) ในรายการ หากไม่มีองค์ประกอบอยู่ในรายการ วิธีการจะส่งกลับ -1
public boolean isEmpty();
วิธีการคืนค่าเป็นจริงหากรายการว่างเปล่า (ดูว่าฟิลด์มีค่าเท่ากันหรือไม่size 0
) มิฉะนั้นจะคืนค่าเป็นเท็จ หากรายการมีเพียงองค์ประกอบว่าง วิธีการจะส่งกลับเท็จ กล่าวอีกนัยหนึ่ง องค์ประกอบที่เป็นโมฆะจะถูกนำมาพิจารณาด้วยวิธีนี้ด้วย ค้นหาจำนวนองค์ประกอบในรายการ:
public int size();
ส่งกลับจำนวนองค์ประกอบในรายการ (ค่าฟิลด์ขนาด) จำนวนองค์ประกอบอาจแตกต่างจากความจุรายการ (ความจุ) รับตัววนซ้ำสำหรับรายการ:
public Iterator< E> iterator();
ส่งคืนตัววนซ้ำสำหรับรายการเพื่อใช้ในภายหลังในลูปหรือการประมวลผลอื่น ๆ ตัววนซ้ำใช้พฤติกรรมที่ล้มเหลวอย่างรวดเร็ว ถ้ามันทำงานผ่านคอลเลกชันและสังเกตเห็นการแก้ไขบางอย่าง (ที่ไม่ได้รับโดยใช้วิธีการวนซ้ำ) มันจะส่งข้อยกเว้นConcurrentModificationException
ทันที ตัววนซ้ำมีสิ่งที่เรียกว่าmodification count
. เมื่อตัววนซ้ำวนซ้ำคอลเลกชันหลังจากแต่ละรายการnext/hasNext/remove
มันจะตรวจสอบตัวนับนี้ หากไม่ตรงกับสิ่งที่ตัววนซ้ำคาดว่าจะเห็น ก็จะเกิดข้อยกเว้น ฉันจะไม่พิจารณาตัววนซ้ำโดยละเอียดที่นี่
public ListIterator< E> listIterator() и public ListIterator< E> listIterator(int index)
ส่งคืนตัววนซ้ำรายการสำหรับรายการเพื่อใช้ในภายหลังในลูปหรือการประมวลผลอื่น ๆ อินเทอร์เฟซListIterator
ขยายอินเทอร์เฟซIterator
สำหรับการสำรวจรายการแบบสองทางและการแก้ไของค์ประกอบ ในเวอร์ชันโอเวอร์โหลด คุณสามารถส่งผ่านดัชนีที่จะเริ่มต้น "การข้ามผ่าน" ได้ ดัชนีในกรณีนี้หมายถึงองค์ประกอบแรกที่วิธีการจะเริ่มทำงานnext()
และเมื่อมีการเรียกใช้เมธอด การprevious()
สำรวจเส้นทางจะเริ่มต้นจากองค์ประกอบภายใต้ดัชนี "ดัชนีที่ผ่าน - 1"
public Spliterator <E> spliterator()
Java 8 แนะนำรูปแบบใหม่ของการเชื่อมโยงล่าช้าและตัววนซ้ำที่ล้มเหลวอย่างรวดเร็วที่เรียกว่าตัววนซ้ำตัวคั่น ตัววนซ้ำตัวคั่นช่วยให้คุณสามารถวนซ้ำตามลำดับขององค์ประกอบได้ แต่จะมีการใช้ในลักษณะที่แตกต่างออกไป คุณสมบัติที่สำคัญที่สุดของ อินเทอร์ เฟซ Spliteratorคือความสามารถในการรองรับการวนซ้ำแบบขนานของแต่ละส่วนของลำดับขององค์ประกอบ และดังนั้นจึงการเขียนโปรแกรมแบบขนาน
GO TO FULL VERSION