OODS · บทที่ 4 List (รายการ)

หัวข้อ 10 · 15 นาที

List และ ArrayList

นึกภาพก่อน

แถวหนังสือบนชั้นมีลำดับ: เล่มแรก เล่มที่สอง เล่มที่สาม ถ้าจะแทรกเล่มใหม่ไว้เป็นเล่มที่สอง ต้องขยับเล่มที่อยู่ถัดไปทุกเล่มไปทางขวาหนึ่งช่อง และถ้าดึงเล่มหนึ่งออก ต้องขยับเล่มที่เหลือมาปิดช่องว่าง

List (รายการ) คือ Collection ที่ข้อมูลแต่ละตัว มีอันดับ เขียนเป็น ⟨a₀, a₁, ..., aₙ₋₁⟩ ข้อมูลซ้ำได้ ความแตกต่างจาก ArrayCollection คือ ต้องรักษาลำดับ จึงใช้ลูกเล่น "ย้ายตัวท้ายมาแทน" ไม่ได้อีกแล้ว

Interface

java
public interface List extends Collection {
  public void add(int index, Object e);
  public void remove(int index);
  public Object get(int index);
  public void set(int index, Object e);
  public int indexOf(Object e);
}

List extends Collection จึงมี add(e), remove(e), contains, size, isEmpty ด้วย และเพิ่มเมท็อดที่ใช้ตำแหน่ง

ArrayList

ข้อมูลตัวที่ i อยู่ในช่อง elementData[i] เรียงติดกันตั้งแต่ช่อง 0

เมท็อดที่เร็ว

java
public Object get(int index) { return elementData[index]; }      // Θ(1)
public void set(int index, Object e) { elementData[index] = e; }  // Θ(1)
public void add(Object e) { add(size, e); }                       // ต่อท้าย

add(index, e)

java
public void add(int index, Object e) {
  ensureCapacity(size + 1);
  for (int i = size; i > index; i--)
    elementData[i] = elementData[i - 1];
  elementData[index] = e;
  size++;
}

เลื่อนข้อมูลตั้งแต่ตำแหน่ง index ไปทางขวาหนึ่งช่อง เริ่มจากตัวท้ายสุด แล้วจึงใส่ข้อมูลใหม่ ใช้เวลา O(n)

  • add(0, e) ช้าที่สุด (เลื่อนทุกตัว)
  • add(size, e) เร็วที่สุด (ไม่เลื่อนเลย)

remove(index)

java
public void remove(int index) {
  for (int i = index + 1; i < size; i++)
    elementData[i - 1] = elementData[i];
  size--;
  elementData[size] = null;
}

เลื่อนข้อมูลที่อยู่หลัง index มาทางซ้ายหนึ่งช่อง เริ่มจากตัวที่ติดกับช่องที่ลบ ใช้เวลา O(n)

  • remove(0) ช้าที่สุด
  • remove(size - 1) เร็วที่สุด

remove(e) และ indexOf

java
public void remove(Object e) {
  int i = indexOf(e);
  if (i >= 0) remove(i);
}

indexOf ไล่หาจากช่อง 0 ใช้ O(n) แล้วเรียก remove(i) ที่เขียนไว้

ช่วง index ที่ถูกต้อง

สไลด์มี quiz สองข้อเรื่องนี้:

เมท็อดช่วงที่ใช้ได้เหตุผล
add(index, e)0 ถึง sizeindex = size คือต่อท้าย
remove(index), get, set0 ถึง size - 1ต้องเป็นตำแหน่งที่มีข้อมูลอยู่

ตัวอย่างไล่ทีละขั้น

จากสไลด์:

คำสั่งรายการหลังทำหมายเหตุ
List x = new ArrayList(10);⟨ ⟩
x.add(0, "A");⟨A⟩
x.add("B");⟨A, B⟩ต่อท้าย
x.add(0, "C");⟨C, A, B⟩A และ B เลื่อนขวา
x.set(2, "D");⟨C, A, D⟩แทนที่ B
i = x.indexOf("A");i = 1
x.add(i, "Z");⟨C, Z, A, D⟩A และ D เลื่อนขวา
x.remove(2);⟨C, Z, D⟩ลบ A แล้ว D เลื่อนซ้าย

จบแล้ว x.size() ได้ 3

Simulation แสดงการเลื่อนทีละตัวพร้อมนับจำนวนครั้ง ลองตัวอย่าง "เพิ่มหน้า vs เพิ่มท้าย" เพื่อเทียบ

ขั้นที่ 1 / 12เริ่มต้น

ใช้ได้: add <ค่า>, add <i> <ค่า>, set <i> <ค่า>, remove <i>, get <i>

size = 0

01234567
  1. add 0 A
  2. add B
  3. add 0 C
  4. set 2 D
  5. add 1 Z
  6. remove 2

ArrayList.java

public void add(int index, Object e) {
ensureCapacity(size + 1);
for (int i = size; i > index; i--)
elementData[i] = elementData[i - 1];
elementData[index] = e;
size++;
}
เส้นประ = ค่าเดิมที่ถูกคัดลอกไปแล้วลูกศร = การย้ายหนึ่งครั้ง
1.0×

คำอธิบายทีละขั้น

สถานะเริ่มต้น: รายการว่าง

ArrayList เก็บข้อมูลตัวที่ i ไว้ในช่อง i ของอาเรย์ ข้อมูลจึงต้องอยู่ติดกันและเรียงตามอันดับเสมอ ต่างจาก ArrayCollection ที่สลับลำดับได้ (อาเรย์ในภาพมี 8 ช่อง)

เวลาการทำงาน

เมท็อดเวลา
get(i), set(i, e)Θ(1)
add(e) (ต่อท้าย)Θ(1) ถ้าไม่ต้องขยายอาเรย์
add(i, e), remove(i)O(n)
indexOf(e), contains(e), remove(e)O(n)

จุดที่มักพลาด

1. เลื่อนผิดทิศตอน add

ลูปของ add ต้องเริ่มจากท้าย (i = size ลดลง) ถ้าเริ่มจากหน้า ค่าในช่องถัดไปจะถูกเขียนทับก่อนได้ย้าย ทุกช่องจะกลายเป็นค่าเดียวกัน

2. คิดว่า add ใช้ index ได้ถึง size − 1

add(size, e) ใช้ได้และหมายถึงต่อท้าย

3. คิดว่า remove ใช้ index ได้ถึง size

ไม่ได้ ช่อง size ไม่มีข้อมูล

4. สับสน remove(int) กับ remove(Object)

remove(2) ลบตำแหน่ง 2 ส่วน remove("A") ลบข้อมูล A ตัวแรกที่พบ

5. ใช้ลูกเล่นของ ArrayCollection กับ ArrayList

การย้ายตัวท้ายมาแทนช่องที่ลบทำให้ลำดับเสีย List จึงต้องเลื่อนทีละตัว

6. คิดว่า set เปลี่ยนขนาด

set แทนที่ข้อมูลเดิม size ไม่เปลี่ยน ต่างจาก add(i, e) ที่แทรกและทำให้ size เพิ่ม

ที่มา: LIST_4.pdf หน้า 1–16