จากหัวข้อก่อนหน้านี้เราได้เรียนรู้เกี่ยวกับอาเรย์ซึ่งเป็นชุดข้อมูลพื้นฐานที่มีมากับภาษาจาวา (core java) แต่ด้วยข้อจำกัดของอาเรย์ในหลายๆด้าน เช่น ขนาดของอาเรย์ไม่สามารถเปลี่ยนแปลงได้ หากเราต้องการเพิ่มขนาดของ
อาเรย์เราต้องสร้างอาเรย์ใหม่ที่ใหญ่ขึ้นกว่าเดิมแล้วคัดลอกข้อมูลในอาเรย์เดิมมายังอาเรย์ใหม่เอง ผู้พัฒนาภาษาจาวาจึงสร้างคลาสที่เป็นชุดข้อมูล (data collection) ในแบบต่างๆซึ่งมีความยืดหยุ่นในการใช้งานมากกว่าอาเรย์ขึ้นมาให้เราเลือกใช้งานโดยแบ่งออกเป็นชุดข้อมูลแบบ Set List Queue Deque และ Map
และเพื่อให้การใช้งานชุดข้อมูลดังกล่าวและชุดข้อมูลที่อาจจะมีการพัฒนาเพิ่มเติมในอนาคตมีมาตรฐานในการใช้งานแบบเดียวกัน ผู้พัฒนาภาษาจาวาจึงกำหนดกรอบการทำงาน (framework) ในการสร้างคลาสของชุดข้อมูลขึ้นมาเรียกว่า Collection Framework
Collection Framework ถูกกำหนดขึ้นมาเพื่อให้คลาสของชุดข้อมูลแบบต่างๆมีการใช้งานที่เหมือนกัน สามารถทำความเข้าใจการใช้งานได้ง่าย ใช้เมธอดชื่อเดียวกันในการจัดการข้อมูล เช่น ชุดข้อมูลทุกแบบจะใช้เมธอด add() ในการเพิ่มข้อมูลเข้าไปในชุดข้อมูล และใช้เมธอด get() ในการอ่านค่าข้อมูลจากชุดข้อมูล เป็นต้น ส่วนความแตกต่างในการทำงานของชุดข้อมูลแต่ละชนิดจะอยู่ในชุดคำสั่งภายใต้เมธอดของแต่ละคลาส ซึ่งในการใช้งานเราไม่จำเป็นต้องสนใจตรงนี้ ขอเพียงเข้าใจวิธีการในการจัดการชุดข้อมูลแต่ละแบบเพื่อเลือกใช้ให้ถูกต้องก็พอ และ Collection Framework ยังเป็น java generics ซึ่งผู้ใช้งานสามารถปรับเปลี่ยนชนิดของชุดข้อมูลที่ใช้ได้ในภายหลังโดยไม่กระทบกับโปรแกรมอื่นที่มาเรียกใช้งาน
สำหรับอาเรย์นั้นถึงแม้ว่าจะไม่ได้อยู่ใน Collection Framework แต่ Collection Framework ก็มีเมธอดให้เราสามารถย้ายข้อมูลเข้า/ออกระหว่าง
อาเรย์กับรูปแบบของชุดข้อมูลแบบต่างๆใน Collection Framework
จากภาพด้านบนจะเห็นว่ามีการประกาศคลาสอินเตอร์เฟส Collection และ คลาสอินเตอร์เฟส List มีการกำหนด <E> ซึ่งก็คือรองรับการกำหนดพารามิ
เตอร์ไรซ์ไว้ด้วย หมายถึงคลาสอินเตอร์เฟสดังกล่าวเป็น java generics นั่นเอง
โครงสร้างของ Collection Framework
โครงสร้างของ Collection Framework คือการใช้คลาสแบบอินเตอร์เฟสเป็นตัวกำหนดมาตรฐานของชื่อเมธอดที่ต้องมีในคลาสต่างๆที่สืบทอดลงมา สิ่งที่ทำให้คลาสชุดข้อมูลแต่ละแบบมีวิธีการในการจัดการข้อมูลแตกต่างกันคือการใช้ความสามารถในการโอเวอร์โหลดหรือโอเวอร์ไรด์ของเมธอดในแต่ละคลาสลูกที่สืบทอดมาทำให้เกิดความแตกต่างในการทำงานของโครงสร้างข้อมูลแต่ละแบบ
ภาพแสดงโครงสร้างของ Collection Framework
จากภาพด้านบนเป็นโครงสร้างของ Collection Framework โดยส่วนบนสุดของโครงสร้างคือคลาสอินเตอร์เฟส Collection และในลำดับชั้นถัดมาเป็นคลาสอินเตอร์เฟสช Set List Queue และ Dequeue ซึ่งแยกย่อยออกมาตามลักษณะของการจัดการข้อมูลที่แตกต่างกันไปและภายใต้คลาสอินเตอร์เฟสย่อยเหล่านั้นคือคลาสที่เราใช้งาน เช่น คลาส ArrayList และคลาส Linked List จะอยู่ภายใต้คลาสอินเตอร์เฟส List ส่วนคลาสอินเตอร์เฟส Map ถึงแม้ว่าจะเป็นคลาสอินเตอร์เฟสที่แยกออกมาต่างหากจากคลาสอินเตอร์เฟส Collection แต่ก็ถูกกำหนดให้อยู่ใน Collection Framework โดยภายใต้คลาสอินเตอร์เฟส Map จะมีคลาส เช่น คลาส HashMap และมีคลาสอินเตอร์เฟสลูกคือ คลาส SortedMap
ความแตกต่างของชุดข้อมูลแต่ละแบบ
ชุดข้อมูลภายใต้คลาสอินเตอร์เฟส Collection แบ่งออกเป็น 4 แบบคือ Set List Queue และ Deque และยังมีชุดข้อมูลที่แยกต่างหากจากคือคลาสอินเตอร์เฟส Map
Set เป็นชุดข้อมูลที่ไม่ยอมให้มีข้อมูลในกลุ่มซ้ำ เหมาะสำหรับใช้กับกลุ่มข้อมูลที่ไม่มีข้อมูลซ้ำกัน
List เป็นชุดข้อมูลที่ข้อมูลสามารถซ้ำกันได้ โดยข้อมูลจะถูกนำมาเรียงต่อกันและสามารถอ้างอิงถึงข้อมูลได้ด้วยลำดับของข้อมูลซึ่งเรียกว่าดัชนี
Queue เป็นชุดข้อมูลที่เก็บข้อมูลในรูปแบบการเข้าแถวโดยข้อมูลที่เข้ามาในชุดข้อมูลก่อนจะถูกนำไปดำเนินการก่อน (first in first out, FIFO) มักจะใช้เก็บข้อมูลที่รอการดำเนินการ
Deque คล้ายกับ Queue แต่สามารถรองรับการเก็บข้อมูลในลักษณะมาทีหลังไปก่อนได้ด้วย (last in first out, LIFO)
Map เป็นชุดข้อมูลที่จับคู่ระหว่างข้อมูลกับคีย์ โดยคีย์ห้ามซ้ำกันแต่ข้อมูลซ้ำกันได้ การเข้าถึงข้อมูลจะใช้การอ้างอิงโดยใช้คีย์
การดำเนินการชุดข้อมูลด้วยคลาส ArrayList
การใช้งานอาเรย์ซึ่งเป็นชุดข้อมูลพื้นฐานที่มีมากับภาษาจาวา (core java) จะมีข้อจำกัดตรงที่ขนาดของอาเรย์ไม่สามารถลดหรือเพิ่มขนาดได้ ดังนั้นหากต้องการใช้งานอาเรย์ที่สามารถปรับขนาดได้เราจะใช้คลาส ArrayList
คลาส ArrayList เป็นคลาสลูกของอินเตอร์เฟส List คลาส Arraylist จะเก็บข้อมูลเป็นลำดับและสามารถอ้างถึงข้อมูลได้ด้วยดัชนีเช่นเดียวกับอาเรย์
การใช้งานคือ import แพคเกจ java.util.ArrayList และสร้างออบเจกต์ของคลาส ArrayList ขึ้นมา โดยชนิดข้อมูลที่สามารถเก็บในอาเรย์ลิสต์จะต้องเป็นออบเจกต์เท่านั้น หรือเราอาจจะรียกใช้งานอาเรย์ลิสต์และให้โปรแกรม intelliJ IDEA เพิ่มแพคเกจของคลาส ArrayList ให้เราเองก็ได้
เนื่องจากคลาส ArrayList รองรับความสามารถจาวาเจเนริค เราสามารถสร้างออบเจกต์จากคลาส ArrayList ได้ทั้งแบบระบุหรือไม่ระบุชนิดของออบเจกต์ที่ต้องการเก็บในอาเรย์ลิสต์
ตัวอย่างด้านล่างเป็นการใช้งานแบบไม่ระบุชนิดของออบเจกต์ที่ต้องการเก็บในอาเรย์ลิสต์โดยสร้างอาเรย์ลิสต์ขึ้นมาเพื่อเก็บตัวเลข สังเกตุว่าในคำสั่ง for เราสามารถอ้างถึงข้อมูลแต่ละตัวในอาเรย์ลิสต์แบบกว้างๆว่า Object ได้ แต่ในการสั่งพิมพ์เราต้อง cast ข้อมูลด้วยคลาส Integer เพื่อบอกว่าออบเจตก์นั้นคืออะไรเพื่อให้สามารถพิมพ์ผลลัพธ์ออกมาได้
การใช้คลาส ArrayList แบบไม่ระบุชนิดของออบเจกต์ที่ต้องการเก็บในอาเรย์ลิสต์เรียกว่าการใช้งานแบบ raw generics ซึ่งไม่แนะนำให้ใช้แบบนี้เนื่องจากอาจจะทำให้เกิดข้อผิดพลาดในขณะใช้งาน (run-time error) ดังนั้นเราควรใช้แบบระบุชนิดของออบเจกต์ที่ต้องการเก็บในอาเรย์ลิสต์เสมอ ซึ่งนอกจากจะลดข้อผิดพลาดแล้วยังได้ใช้ความสามารถของโปรแกรม IntelliJ IDEA ในการตรวจสอบความถูกต้องของการเรียกใช้งานอาเรย์ลิสต์ในส่วนต่างๆของโปรแกรมด้วย
ตัวอย่างด้านล่างเป็นการสร้างออบเจกต์อาเรย์ลิสต์แบบระบุชนิดของออบเจกต์ที่ต้องการเก็บในอาเรย์ลิสต์ โดยระบุชนิดของออบเจกต์ในเครื่องหมาย < > ซึ่งในที่นี้คือการระบุว่าชนิดของออบเจกต์คือ String
ประโยค new ArrayList< >() ก็คือการเรียกเมธอดคอนสตรัคเตอร์ของคลาส ArrayList เพื่อสร้างออบเจกต์ที่เป็นอาเรย์ลิสต์ จากตัวอย่างด้านบนเราสร้างออบเจกต์ที่เป็นอาเรย์ลิสต์เพื่อเก็บชนิดข้อมูล String และเก็บออบเจกต์อาเรย์
ลิสต์ไว้ที่ตัวแปร stringArrayList
เราสามารถใช้ String กับอาเรย์ลิสต์ได้เพราะ String เป็น reference data type ไม่ใช่ primitive data type หากเราต้องการเก็บชนิดข้อมูลแบบ primitive data type เราจะต้องระบุชนิดของออบเจกต์ที่ต้องการเก็บด้วย Wrapper Class ของชนิดข้อมูล primitive data type นั้นๆ เช่น Integer หรือ Double เพื่อแปลงชนิดข้อมูลแบบ primitive data type ไปเป็นชนิดข้อมูลแบบ reference data type หรือออบเจกต์เสียก่อน วิธีการคือระบุ Wrapper Class ของชนิดข้อมูล primitive data type ที่ต้องการในเครื่องหมาย < > ดังตัวอย่างด้านล่าง
การจัดการอาเรย์ลิสต์ทำโดยใช้เมธอดของคลาส ArrayList เช่น การใช้เมธอด add(object) เพื่อเพิ่มข้อมูลในอาเรย์ลิสต์ตัวอย่างเช่น
เมธอด add() จะมี 2 แบบคือ add(object) ซึ่งจะได้ผลลัพธ์เป็นชนิดข้อมูลแบบ boolean คือ true ถ้าเพิ่มข้อมูลสำเร็จหรือ false ถ้าเพิ่มข้อมูลไม่สำเร็จ ดังนั้นเราจึงสามารถสร้างตัวแปรด้วยชนิดข้อมูลแบบ boolean เพื่อมารับค่าเอาไปใช้งานต่อได้ หรือหากไม่สนใจผลลัพธ์ก็ไม่จำเป็นต้องสร้างตัวแปรมาเก็บค่าก็ได้ ดังตัวอย่างด้านบน ส่วน add(index,object) นั้นเราสามารถระบุตำแหน่งดัชนีที่ต้องการเก็บข้อมูลได้แต่จะไม่มีผลลัพธ์ใดๆกลับมา (void)
สำหรับการเพิ่มข้อมูลแบบ primitive data type ในอาเรย์ลิสต์ เมื่อเราระบุชนิดข้อมูลที่จะเก็บในอาเรย์ลิสต์ด้วย Wrapper Class ของข้อมูลแบบ primitive data type ที่ต้องการแล้ว เราสามารถระบุข้อมูลที่จะเพิ่มในอาเรย์ลิสต์ได้โดยตรงในเมธอด add() ซึ่งภาษาจาวาจะจัดการแปลงชนิดข้อมูลแบบ primitive data type ไปเป็นชนิดข้อมูลแบบ reference data type หรือออบเจกต์เสียก่อนแล้วจึงจัดเก็บในอาเรย์ลิสต์ เราเรียกการดำนินการแบบนี้ว่าเป็นการทำ Autoboxing ซึ่งจากตัวอย่างด้านล่างเป็นการเพิ่มตัวเลข 1 และตัวเลข 1.12 ในอาเรย์ลิสต์
การทำ Autoboxing เหมือนเป็นการใช้ Wrapper Class หุ้มข้อมูลแบบ primitive data type ไว้ให้กลายเป็นชนิดข้อมูลแบบ reference data type จึงสามารถเก็บในอาเรย์ลิสต์ได้
เมธอด get(index) ใช้เพื่อค้นคืนข้อมูลจากตำแหน่งตามดัชนีที่ระบุ โดยจะได้ผลลัพธ์เป็นออบเจกต์ตามชนิดของออบเจกต์เราเก็บไว้ในอาเรย์ลิสต์ ดังนั้นเราต้องสร้างตัวแปรที่มีชนิดข้อมูลในแบบเดียวกันมารับไว้ ตัวอย่างเช่น
สำหรับการใช้ชนิดข้อมูลแบบ Wrapper Class ของชนิดข้อมูลแบบ primitive data type การใช้เมธอด get(index) ก็ใช้งานในลักษณะเดียวกัน จากตัวอย่างด้านล่างจะเห็นว่าเราสามารถเก็บค่าที่ได้จากอาเรย์ลิสต์ในตัวแปรแบบ primitive data type ได้เลยเพราะภาษาจาวาจัดการแปลงชนิดข้อมูลจาก reference data type ไปเป็นชนิดข้อมูลแบบ primitive data type ให้ เราเรียกการดำนินการแบบนี้ว่าเป็นการทำ Unboxing
การทำ Unboxing เป็นการปลดข้อมูลแบบ primitive data type ออกจาก Wrapper Class
เมธอด remove(index) ใช้เพื่อลบข้อมูลออกจากอาเรย์ลิสต์ โดยระบุดัชนีที่ต้องการลบข้อมูลออก และได้ผลลัพธ์เป็นออบเจกต์ที่เราเอาออกไป หากเราไม่สนใจสมาชิกที่เอาออกก็ไม่ต้องสร้างตัวแปรมารับข้อมูลก็ได้ ตัวอย่างเช่น
เมธอด size() ใช้เพื่อแสดงขนาดของอาเรย์ลิสต์ ได้ผลลัพธ์เป็นเลขจำนวนเต็มเช่น
เมธอด indexOf(object) ใช้เพื่อแสดงดัชนีของข้อมูลที่ต้องการ หากหาข้อมูลที่ต้องการไม่พบจะคืนค่าเป็น -1 ตัวอย่างการใช้งานเช่น
ตัวอย่างด้านล่างเป็นการใช้คลาสที่เราสร้างขึ้นมาเองกับอาเรย์ลิสต์ เราสร้างคลาส Employee และสร้างอาเรย์ลิสต์เพื่อเก็บออบเจกต์ที่สร้างจากคลาส Employee สังเกตุการเรียกใช้เมธอดคอนสตรัคชั่นเพื่อสร้างออบเจกต์ Employee ในเมธอด add() ในบรรทัดที่ 10 -11 ของไฟล์ Main.java
ในบรรทัดที่ 21 เราใช้เมธอด indexOf(object) เพื่อค้นหาชื่อที่ต้องการแต่ผลลัพธ์ที่ได้คือไม่พบ (-1) ทั้งนี้เกิดจากเมธอด indexOf(object) ไม่รู้จักออบเจกต์ของเราจึงไม่สามารถใช้งานกับคลาสที่เราสร้างขึ้นมาเองได้โดยตรง (เราใช้เมธอด indexOf(object) กับ String ได้เพราะมีการเขียนโปรแกรมรองรับไว้ในคลาส String แล้ว)
หากเราต้องการค้นข้อมูลในอาเรย์ลิสต์ที่เก็บออบเจกต์ที่เราสร้างขึ้นมา เราต้องใช้คำสั่ง for เพื่อวนลูปค้นหาเอง หรือใช้การโอเวอร์ไรด์เมธอด indexOf(object) เพื่อให้รู้จักออบเจกต์ของเรา แต่ในที่นี้เราจะใช้คำสั่ง for เพื่อวนลูปค้นหาเอง ดังตัวอย่างด้านล่าง
ในกรณีที่เราต้องการใช้คำสั่ง for เพื่อวนลูปอ่านข้อมูลทั้งอาเรย์ลิสต์ สามารถทำได้ดังตัวอย่าง
หากเราต้องการค้นหาออบเจกต์ที่ต้องการโดยเปรียบเทียบกับข้อมูลที่กำหนด เช่น ค้นหาด้วยชื่อพนักงาน สามารถทำได้ดังตัวอย่างด้านล่างโดยใช้ for each และเมธอด eguals(object)
จากตัวอย่างที่ผ่านมาเราจะอ้างถึงสมาชิกในอาเรย์ลิสต์เพื่ออ่านค่าจากฟิลด์ของออบเจกต์โดยใช้เมธอด get(index) ตามด้วยเมธอดในการอ่านข้อมูลของออบเจกต์ เช่น getSalary() ในกรณีที่เราใช้เมธอด get(index) เฉยๆจะเป็นการอ้างถึงตัวออบเจกต์ จากตัวอย่างด้านล่าง เราสร้างตัวแปร tempEmployee ด้วยชนิดข้อมูล Employee เพื่อเก็บออบเจกต์ที่ได้จาก myemployees(0) (อันที่จริงคือชี้ไปยังที่เก็บข้อมูลเดียวกันในหน่วยความจำ)
นอกจากการวนลูปโดยการใช้ดัชนีแล้ว เราสามารถใช้คำสั่ง for each เพื่อวนลูปโดยใช้การอ้างอิงออบเจกต์แทนที่การใช้ดัชนีได้ดังตัวอย่างด้านล่าง
เนื่องจากอาเรย์ลิสต์ถูกออกแบบมาให้เหมือนอาเรย์คือมีการจองพื้นที่เก็บข้อมูลต่อเนื่องติดๆกัน ดังนั้นการแทรกหรือลบข้อมูลในอาเรย์ลิสต์จะส่งผลกระทบกับข้อมูลทั้งหมดที่จะต้องเขยิบเข้าหรือเขยิบออก ซึ่งหากข้อมูลของเรามีจำนวนมากเป็นหมื่นเป็นแสนรายการจะทำให้การทำงานช้าลงและกระทบประสิทธิภาพของเครื่องคอมพิวเตอร์ ดังนั้นหากเราต้องแทรกหรือลบข้อมูลบ่อยๆอาจจะพิจารณาใช้การจัดการชุดข้อมูลแบบอื่น เช่น คลาส LinkedList เป็นต้น แต่หากเราใช้งานในลักษณะที่เป็นการอ่านข้อมูลต่อเนื่องกันการใช้อาเรย์ลิสต์จะเร็วกว่า
การดำเนินการชุดข้อมูลด้วยคลาส LinkedList
คลาส LinkedList ก็เป็นคลาสลูกของคลาสแบบอินเตอร์เฟส List เช่นเดียวกับคลาส ArrayList ดังนั้นการใช้งานจะเหมือนกัน ต่างกันที่โครงสร้างของคลาส LinkedList จะเชื่อมต่อข้อมูลแต่ละตัวด้วยลิงค์จากข้อมูลหนึ่งไปยังอีกข้อมูลหนึ่งดังภาพตัวอย่างด้านล่าง แม้ว่าการเชื่อมต่อของลิงค์ลิสต์จะแตกต่างจากอาเรย์ลิสต์แต่เราก็สามารถอ้างถึงข้อมูลโดยใช้ดัชนีได้เช่นกัน
ตัวอย่างด้านล่างเป็นการใช้งานลิงค์ลิสต์ซึ่งจะเหมือนกับการใช้งานอาเรย์ลิสต์เพราะเป็นคลาสลูกของคลาสอินเตอร์เฟส List ซึ่งอยู่ใน Collection Framework เหมือนกัน
ข้อแตกต่างที่สำคัญระหว่างอาเรย์ลิสต์และลิงค์ลิสต์คือการเพิ่มหรือลบข้อมูลจากจากลิงค์ลิสต์จะเป็นการจัดการกับลิงค์ของข้อมูลเพราะข้อมูลไม่ได้ถูกเก็บต่อเนื่องกันเหมือนอาเรย์ลิสต์ ดังนั้นจะไม่เกิดการขยับข้อมูลทั้งหมดเมื่อมีการแทรกข้อมูลหรือลบข้อมูลตรงกลางจากตรงกลางของลิงค์ลิสต์ แต่หากเราใช้งานในลักษณะที่เป็นการอ่านข้อมูลต่อเนื่องกันการใช้อาเรย์ลิสต์จะเร็วกว่า
การใช้ ListIterator เพื่อเข้าถึงแต่ละสมาชิกในลิสต์
ในการเข้าถึงข้อมูลของแต่ละสมาชิกในลิสต์ นอกจากการใช้คำสั่ง for แล้ว เราสามารถใช้ออบเจกต์ชนิด listiterator ในการเข้าถึงสมาชิกแต่ละตัวในลิสต์
การสร้างออบเจกต์ชนิด listiterator ทำได้โดยเรียกใช้เมธอด listIterator() ซึ่งเป็นเมธอดมาตรฐานของชนิดข้อมูลแบบลิสต์ซึ่งจะมีอยู่ทั้งในคลาส ArrayList และ คลาส LinkedList
การใช้เมธอด listIterator() จะให้ผลลัพธ์เป็นออบเจกต์ชนิด listiterator ซึ่งเมธอด listIterator() นี้สืบทอดมาตั้งแต่คลาสอินเคอร์เฟสลิสต์ซึ่งจะเป็นการ เรียกใช้งานคลาสแบบอินเตอร์เฟส ListIterator โปรแกรม intelliJ IDEA จะ import แพคเกจของคลาสแบบอินเตอร์เฟส ListIterator มาให้โดยอัตโนมัติ เมื่อเราเรียกใช้งานเมธอด listIterator() ดังตัวอย่างด้านล่าง
การใช้งานเมธอด listIterator() ทำโดยประกาศตัวแปรที่เป็นชนิดข้อมูลแบบ ListIterator และระบุชนิดของออบเจกต์ที่เก็บในลิสต์ไว้ในเครื่องหมาย < > และเรียกใช้เมธอด listIterator() โดยอ้างอิงจากออบเจกต์ของลิสต์ จากตัวอย่างด้านล่าง เป็นการประกาศตัวแปร i เพื่อเก็บออบเจกต์ listiterator ของออบเจกต์ลิงค์ลิสต์ที่เก็บในตัวแปร myLinkedList โดยกำหนดชนิดข้อมูลสำหรับตัวแปร i เป็น ListIterator และระบุพารามิเตอร์ไรซ์เป็นคลาส String
เมื่อเราสร้างออบเจกต์ชนิด listiterator ขึ้นมาจะเหมือนเรามีตัวชี้ตำแหน่งเพื่อให้เราอ้างอิงถึงตำแหน่งของข้อมูลในลิสต์โดยให้จินตนาการว่าตัวชี้ตำแหน่งจะอยู่ในตำแหน่งของลิงค์ที่ใช้เชื่อมตำแหน่งที่เก็บข้อมูลแต่ละตัวเสมอ ไม่ใช่อยู่ที่ตำแหน่งที่เก็บข้อมูล การอ่านข้อมูลจะเป็นการอ่านพร้อมกับการเคลื่อนที่ของตัวชี้ข้อมูลไปยังตำแหน่งของลิงค์ถัดไป เช่น ใช้เมธอด next()
เพื่อไปข้างหน้าพร้อมกับอ่านข้อมูล หรือใช้เมธอด previous() เพื่อย้อนกลับพร้อมกับอ่านข้อมูล ตัวอย่างเช่น
แรกเริ่มตัวชี้ตำแหน่งจะอยู่ในตำแหน่งแรกสุด
เมื่อเราใช้เมธอด next() เช่น เมื่อเราใช้ประโยคคำสั่ง System.out.println(i.next()); จะพิมพ์เลข 1 และเลื่อนตัวชี้ตำแหน่งไปข้างหน้าดังนี้
เมื่อเราใช้ประโยคคำสั่ง System.out.println(i.next()); อีกครั้งจะพิมพ์เลข 2 และเลื่อนตัวชี้ตำแหน่งไปข้างหน้าดังนี้
เมื่อเราใช้เมธอด previous() เช่น เมื่อเราใช้ประโยคคำสั่ง System.out.println(i.previous()); จะพิมพ์เลข 2 และเลื่อนตัวชี้ตำแหน่งย้อนกลับดังนี้
ตัวอย่างเช่น
เราสามารถตรวจสอบได้ว่ามีข้อมูลอยู่ในตำแหน่งถัดไปหรือไม่ด้วยเมธอด hasnext() หรือหากย้อนกลับไปจะมีข้อมูลหรือไม่ด้วยเมธอด hasprevious() ซึ่งทั้ง 2 เมธอดจะคืนค่ากลับมาเป็นชนิดข้อมูล boolean คือ true หรือ false
เราสามารถตรวจสอบดัชนีของข้อมูลได้โดยใช้เมธอด previousIndex() หรือ nextIndex()
เราสามารถใช้งานข้อมูลในลิสต์ได้โดยการเลื่อนตัวชี้จาก listiterator ไปยังตำแหน่งที่ต้องการและอ้างอิงถึงข้อมูลในตำแหน่งนั้นและเรียกใช้เมธอดของ
ออบเจกต์ที่เก็บอยู่ในลิงค์ลิสต์อีกทีหนึ่ง ดังตัวอย่างด้านล่าง เราสร้างคลาส Employee ขึ้นมาเพื่อเก็บข้อมูลของพนักงาน
เราสร้างออบเจกต์ Employee ขึ้นมาดังบรรทัดที่ 10-12 จากนั้นเราสร้างออบเจกต์ listiterator ดังบรรทัดที่ 14 ซึ่งตัวชี้จะอยู่ที่ตำแหน่งเริ่มต้น ในบรรทัดที่ 15 เราใช้คำสั่ง while เพื่อวนลูปตรวจสอบว่ายังมีข้อมูลอยู่และในบรรทัดที่ 16 เราอ่านข้อมูลโดยการเลื่อนตัวชี้และอ้างถึงเมธอดของออบเจกต์ที่เก็บอยู่ในลิสต์ดังตัวอย่างด้านล่าง
จากตัวอย่างด้านล่าง เราสามารถอ้างอิงด้วยดัชนีได้โดยการอ่านค่าดัชนีของลิสต์ดังตัวอย่างในบรรทัดที่ 16 และสั่งพิมพ์ข้อมูลโดยการอ้างอิงดัชนีดังตัวอย่างในบรรทัดที่ 17 จากนั้นจึงเลื่อนตัวชี้ไปยังตำแหน่งถัดไปดังตัวอย่างในบรรทัดที่ 20
การยืดหยุ่นการดำเนินการชุดข้อมูลด้วยอินเตอร์เฟส List และ ListIterator
จากหัวข้อที่ผ่านมาเราได้รู้จักกับคลาส ArrayList และ คลาส LinkedList ซึ่งทั้ง 2 คลาสเป็นคลาสลูกของคลาสแบบอินเตอร์เฟส List ซึ่งเมธอดของทั้ง 2 คลาสจะเหมือนกันเพราะว่าถูกบังคับมาจากคลาสแบบอินเตอร์เฟส List เช่นเดียวกัน
ดังนั้นเราสามารถเขียนโปรแกรมให้ยืดหยุ่นมากขึ้นสำหรับเผื่อทางเลือกในการแก้ไขในอนาคตโดยที่ไม่กระทบกับโปรแกรมหรือคลาสอื่นที่มาเรียกใช้คลาสของเรา โดยการกำหนดชนิดข้อมูลของตัวแปรที่จะสร้างขึ้นมาเป็น List แทนที่จะกำหนดอย่างเฉพาะเจาะจงว่าเป็น ArrayList หรือ LinkedList (เป็นการใช้คุณสมบัติ polymorphism) ส่วนการจัดการข้อมูลเช่น ค้นหา เพิ่ม ลบ แก้ไข ให้ใช้คลาสแบบอินเตอร์เฟส ListIterator ดังตัวอย่างด้านล่าง ในบรรทัดแรกเราสร้างออบเจกต์อาเรย์ลิสต์และเก็บในตัวแปรที่กำหนดชนิดข้อมูลเป็น List
แต่ต่อมาเราพบว่ามีการแทรกหรือลบข้อมูลในช่วงกลางของลิสต์มากขึ้นทำให้การทำงานช้าลง เราก็สามารถเปลี่ยนไปใช้ลิงค์ลิสต์ได้ตามตัวอย่างด้านล่างโดยที่ไม่ต้องแก้ไขโปรแกรมส่วนอื่นๆและผลลัพธ์ยังคงได้เหมือนเดิม และหลังจากที่เราคอมไพล์และนำไปติดตั้งใหม่แล้ว โปรแกรมอื่นที่เรียกใช้งานโปรแกรมของเราก็ไม่มีผลกระทบอะไรเพราะการเรียกใช้งานยังเหมือนเดิม
การสำเนาชุดข้อมูลประเภทลิสต์ด้วยเมธอดคอนสตรัคเตอร์และข้อควรระวัง
เราสามารถสำเนาข้อมูลประเภทลิสต์ (List) ผ่านการใช้เมธอดคอนสตรัคเตอร์ได้โดยการสั่งสร้างชุดข้อมูลใหม่โดยการอ้างอิงชุดข้อมูลเก่า ตัวอย่างเช่น
จากตัวอย่างด้านบน ในบรรทัดที่ 8 เป็นการสร้างอาเรย์ลิสต์ที่มีสมาชิกเป็นออบเจกต์จากคลาส Integer บรรทัดที่ 9 เป็นการเพิ่มสมาชิกลงในอาเรย์ลิสต์ และในบรรทัดที่ 10 เป็นการสร้างอาเรย์ลิสต์ใหม่โดยอ้างถึงอาเรย์ลิสต์ a ซึ่งหมายถึงข้อมูลแต่ละตัวในอาเรย์ลิสต์ a จะถูกส่งเข้ามาที่อาเรย์ลิสต์ b ผ่าน
ทางเมธอดคอนสตรัคเตอร์ของคลาส Integer
แต่การสำเนาข้อมูลแบบลิสต์ เช่น อาเรย์ลิสต์ ลิงค์ลิสต์ ด้วยวิธีการนี้จะให้ผลลัพธ์ที่แตกต่างกันระหว่างการระบุชนิดข้อมูลเป็น Wrapper Class กับการระบุชนิดข้อมูลเป็นคลาสที่เราสร้างขึ้นมาเอง
จากตัวอย่างด้านล่าง ในบรรทัดที่ 6-7 เราสร้างลิงค์ลิสต์ a เพื่อเก็บออบเจกต์ที่เป็น Wrapper Classs ในบรรทัดที่ 9 สร้างลิงค์ลิสต์ b โดยสำเนาข้อมูลจากลิงค์ลิสต์ a ในบรรทัดที่ 12-13 พิมพ์ข้อมูลตั้งต้นจากทั้งลิงค์ลิสต์ a และ b ในบรรทัดที่ 16 เราแก้ข้อมูลลิงค์ลิสต์ a และในบรรทัดที่ 18-19 พิมพ์ข้อมูลออกมาเพื่อเปรียบเทียบกับข้อมูลก่อนหน้า
ในบรรทัดที่ 22-23 เราสร้างลิงค์ลิสต์ c เพื่อเก็บออบเจกต์จากคลาสของเราเอง ในบรรทัดที่ 26 สร้างลิงค์ลิสต์ d โดยสำเนาข้อมูลจากลิงค์ลิสต์ c ในบรรทัดที่ 28-37 พิมพ์ข้อมูลตั้งต้นจากทั้งลิงค์ลิสต์ c และ d ในบรรทัดที่ 40 เราแก้ข้อมูลลิงค์ลิสต์ c และในบรรทัดที่ 42-49 พิมพ์ข้อมูลออกมาเพื่อเปรียบเทียบกับข้อมูลก่อนหน้า
จากตัวอย่างด้านบนจะเห็นว่าในกรณีที่ข้อมูลเป็นออบเจกต์จาก Wrapper Class เมื่อเราแก้ไขค่าในลิงค์ลิสต์ a ก็ไม่ส่งผลกระทบต่อค่าในลิงค์ลิสต์ b
แต่เมื่อเราทำเช่นเดียวกันกับลิงค์ลิสต์ c และ d ซึ่งชนิดข้อมูลเป็นออบ
เจกต์จากคลาสที่เราสร้างขึ้นจะพบว่าการแก้ไขค่าในลิงค์ลิสต์ c ส่งผลให้ค่าในลิงค์ลิสต์ d เปลี่ยนไปด้วย แสดงว่าทั้งลิงค์ลิสต์ c และ d ชี้ไปที่ตำแหน่งเก็บข้อมูลของออบเจกต์เดียวกัน
คลาส Collections
คลาส Collections ประกอบไปด้วยเมธอดเพื่อใช้งานกับชุดของข้อมูลใน collection framework แต่ในที่นี้จะกล่าวถึงเพียงบางเมธอดเท่านั้น
ในกรณีที่ข้อมูลไม่มาก เราสามารถเพิ่มสมาชิกในชุดข้อมูลได้ด้วยเมธอด addAll(ชื่อลิสต์,สมาชิกที่ต้องการเพิ่มในลิสต์) ดังนี้
โดยผลลัพธ์ที่ได้คือ
เราสามารถรียงลำดับข้อมูลได้ด้วยเมธอด sort(ชื่อลิสต์) จากตัวอย่างด้านบนเราสามารถเรียงลำดับสมาชิกในชุดข้อมูลได้ดังนี้
โดยผลลัพธ์ที่ได้คือ
อย่างไรก็ตามในการเรียงลำดับข้อมูลนั้นออบเจกต์ที่เป็นสมาชิกของชุดข้อมูลจะต้องมาจากคลาสที่สืบทอดคลาสแบบอินเตอร์เฟส Comparable<T> หรือ Comparator <T>
จากตัวอย่างด้านบนเรากำหนดให้ออบเจกต์ของคลาส String เป็นสมาชิกของชุดข้อมูลและเราใช้เมธอด sort() ในการเรียงลำดับข้อมูลได้เลยเนื่องจากคลาส String สืบทอดคลาสแบบอินเตอร์เฟส Comparable<T> ไว้แล้ว
ในการค้นหาข้อมูลจากชุดข้อมูลขนาดใหญ่ หากเราค้นหาข้อมูลเรียงลำดับกันไปตั้งแต่ดัชนีที่ 0 ไปเรื่อยๆจนกว่าจะพบข้อมูลที่ต้องการ (brute force search) จะใช้เวลาและทรัพยากรของเครื่องคอมพิวเตอร์มากเกินจำเป็น โดยเฉพาะถ้าข้อมูลอยู่ในส่วนท้ายๆของชุดข้อมูล ดังนั้นเราสามารถค้นหาข้อมูลได้ด้วยเมธอด binarySearch(ชื่อลิสต์, ข้อมูลที่ต้องการหา) ซึ่งจะรวดเร็วกว่า แต่ชุดข้อมูลต้องถูกเรียงลำดับมาแล้ว โดยวิธีการของ binarySearch คือการแบ่งชุดข้อมูลออกเป็น 2 ส่วน และเปรียบเทียบข้อมูลที่ต้องการค้นหาว่าอยู่ในส่วนใดจากนั้นจึงทำซ้ำแบบเดิมไปเรื่อยๆจนพบข้อมูลและคืนค่าเป็นดัชนีที่ข้อมูลอยู่
จากตัวอย่างด้านบนที่ข้อมูลเรียงลำดับแล้ว เราใช้เมธอด binarySearch(ชื่อลิสต์, ข้อมูลที่ต้องการหา) เพื่อค้นหาดัชนีของ Snake ดังนี้
โดยผลลัพธ์ที่ได้คือ
เราสามารถหาค่าสูงสุดและค่าต่ำสุดของชุดข้อมูลโดยใช้เมธอด min(ชื่อลิสต์) และเมธอด max(ชื่อลิสต์) โดยข้อมูลต้องเรียงลำดับมาแล้วดังนี้
โดยผลลัพธ์ที่ได้คือ
การใช้งานคลาส Arrays
คลาส Arrays เป็นคลาสที่ประกอบไปด้วยเมธอดสำหรับใช้งานกับอาเรย์ซึ่งเป็นชุดข้อมูลพื้นฐานของภาษาจาวา (core java) เพื่อให้การใช้งานอาเรย์มีความสะดวกมากขึ้น และเนื่องจากคลาส Arrays เป็นส่วนหนึ่งของ Collection Framework ดังนั้นจึงมีเมธอดที่สามารถใช้งานได้ในชื่อเดียวกันเช่น เมธอด sort() เพื่อเรียงลำดับข้อมูลในอาเรย์ เมธอด binarySearch() เพื่อค้นหาข้อมูลในอาเรย์ด้วยอัลกอริธึมในการค้นหาแบบไบนารี่ เป็นต้น นอกจากนี้ยังมีเมธอดอื่นๆ เช่น เมธอด toString() เพื่อแปลงข้อมูลในอาเรย์ให้เป็นข้อความ เมธอด fill() เพื่อเติมข้อมูลในอาเรย์ เมธอด equals() เพื่อเปรียบเทียบข้อมูลในอาเรย์ เป็นต้น
ตัวอย่างการใช้งานคลาส Arrays
การเปรียบเทียบข้อมูลด้วย Comparable และ Comparator
การเปรียบเทียบข้อมูลเป็นพื้นฐานของการเรียงลำดับและค้นหาข้อมูล เราสามารถบอกได้ว่าข้อมูลหรือออบเจกต์ของเราจะถูกเปรียบเทียบระหว่างกันได้อย่างไรโดยการสืบทอดคลาสแบบอินเตอร์เฟส Comparable หรือคลาสแบบอินเตอร์เฟส Comparator
จากตัวอย่างที่ผ่านๆมาเราสร้างชุดของข้อมูลที่มีสมาชิกเป็นออบเจกต์ของ Wrapper Class ซึ่งคลาสเหล่านั้นสืบทอดการใช้คลาสแบบอินเตอร์เฟส Comparable อยู่แล้ว เราจึงสามารถสั่งเรียงลำดับหรือค้นหาข้อมูลได้
แต่ในกรณีที่เราสร้างคลาสของเราเอง เราต้องสืบทอดคลาสแบบอินเตอร์เฟส Comparable หรือคลาสแบบอินเตอร์เฟส Comparator หากเราต้องการให้ข้อมูลของเราสามารถเปรียบเทียบกันได้ เพื่อที่จะใช้เมธอดในการเรียงลำดับหรือค้นหาได้
ข้อแตกต่างระหว่างคลาสแบบอินเตอร์เฟส Comparable และคลาสแบบอินเตอร์เฟส Comparator คือ ในการใช้งานคลาสแบบอินเตอร์เฟส Comparable เราจะระบุการสืบทอดด้วยคีย์เวิร์ด implement ในขั้นตอนการประกาศคลาสของเราและกำหนดพารามิเตอร์ไรซ์เป็นชื่อคลาสของเรา และเราจะต้องโอเวอร์ไรด์เมธอด compareTo() ไว้ในคลาสของเราเพื่อกำหนดวิธีการเปรียบเทียบออบเจกต์ที่เกิดจากคลาสของเรา นั่นหมายความว่าเราสามารถกำหนดวิธีการเปรียบเทียบข้อมูลได้เพียงแบบเดียว
ตัวอย่างด้านล่างเป็นใช้คลาสแบบอินเตอร์เฟส Comparable สังเกตบรรทัดที่ 29 ซึ่งเป็นการประกาศคลาสและการเรียกใช้คลาสแบบอินเตอร์เฟส Comparable ในบรรทัดที่ 46 – 66 เป็นการโอเวอร์ไรด์เมธอด compareTo() เพื่อให้เราระบุวิธีการเปรียบเทียบสำหรับออบเจกต์ที่สร้างมาจากคลาสของเรา
ส่วนการใช้งานคลาสแบบอินเตอร์เฟส Comparator เราจะต้องสร้างคลาสขึ้นมาต่างหากและระบุการสืบทอดด้วยคีย์เวิร์ด implement และกำหนดพารามิเตอร์ไรซ์เป็นชื่อคลาสของเรา และเราจะต้องโอเวอร์ไรด์เมธอด compare() เพื่อกำหนดวิธีการเปรียบเทียบออบเจกต์ที่เกิดจากคลาสของเรา จะเห็นว่าการเปรียบเทียบจะถูกสร้างเป็นออบเจกต์ comparator แยกออกมาต่างหากอีกทีหนึ่ง ทำให้เรากำหนดรูปแบบการเปรียบเทียบได้มากกว่า 1 แบบ
ตัวอย่างด้านล่างเป็นการสืบทอดคลาสแบบอินเตอร์เฟส Comparator สังเกตุว่าเราสร้างคลาส MyClassSortbyName ซึ่งเป็นการเปรียบเทียบข้อมูลด้วยชื่อ และสร้างคลาส MyClassSortbyLastname ซึ่งเป็นการเปรียบเทียบข้อมูลด้วยนามสกุล ส่วนการใช้งานในบรรทัดที่ 22-23 และ 30-31 จะเห็นว่าเราต้องสร้างออบเจกต์ของ Comparator ขึ้นมาก่อนจากนั้นจึงนำไปใช้กับคลาส Collections.sort
ที่เราสามารถใช้ Collections.sort ได้กับคลาสอินเตอร์เฟส Comparable และ Comparator เพราะเมธอด Collections.sort ถูกโอเวอร์โหลดให้รองรับได้ทั้ง List อย่างเดียวหรือ List และออบเจกต์ comparator เข้ามาทำงานนั่นเอง
โครงสร้างข้อมูลแบบ Set
คลาสแบบอินเตอร์เฟส Set เป็นโครงสร้างข้อมูลใน Collection Framework โดยมีรูปแบบการเก็บข้อมูลเป็นตัวๆโดยที่ข้อมูลดังกล่าวจะมีลักษณะเป็นคีย์คือห้ามข้อมูลภายในเซตเดียวกันซ้ำกัน ภายใต้คลาสแบบอินเตอร์เฟส Set มีทั้งคลาส เช่น HashSet และคลาสอินเตอร์เฟสย่อย (sub-interface) เช่น SortedSet
Set เป็นชุดข้อมูลที่ไม่มีการเรียงลำดับดังนั้นลำดับการพิมพ์รายการข้อมูลจะเปลี่ยนไปมาได้และไม่อิงกับลำดับในการป้อนข้อมูลด้วย ตัวอย่างด้านล่างในบรรทัดที่ 11 เป็นการสร้างชุดข้อมูลแบบ Set เพื่อบันทึกข้อมูลด้วยชนิดข้อมูลแบบ String และในบรรทัดที่ 12-14 เป็นการบันทึกข้อมูลเข้าไปในโครงสร้างโดยใช้เมธอด add() เนื่องจากข้อมูลของชุดข้อมูลแบบ Set เป็นคีย์ซึ่งห้ามซ้ำกัน ดังนั้นหากบันทึกข้อมูลเดียวกันข้อมูลใหม่จะไม่ถูกบันทึก ดังตัวอย่างในบรรทัดที่ 15 ซึ่งเป็นการบันทึกข้อมูล “Vissanu” ซ้ำแต่เมื่อพิมพ์ข้อมูลออกมายังคงมีข้อมูล “Vissanu” เพียงรายการเดียว
สำหรับการลบข้อมูลออกจากชุดข้อมูลใช้เมธอด remove() และระบุข้อมูลที่ต้องการลบ โดยในบรรทัดที่ 33 เป็นตัวอย่างการลบข้อมูลและเมื่อพิมพ์ผลลัพธ์ออกมาจะเห็นว่าข้อมูลที่ลบออกไม่มีอยู่แล้ว
เราสามารถรวมข้อมูล (union) จากทุกเซตที่เราสนใจได้โดยการสร้างเซตใหม่ขึ้นมาดังตัวอย่างในบรรทัดที่ 23 และสำเนาข้อมูลจากเซตใดเซตหนึ่งดังตัวอย่างในบรรทัดที่ 24 จากนั้นใช้เมธอด addAll() ในการสำเนาข้อมูลจากเซตอื่นแต่ละเซตที่ต้องการดังตัวอย่างในบรรทัดที่ 25 ผลลัพธ์ที่ได้คือเซตที่รวมข้อมูลข้อมูลจากทุกเซตโดยข้อมูลที่ได้จะไม่ซ้ำกันโดยดูได้จากข้อมูลที่พิมพ์ออกมา
เราสามารถแยกเฉพาะข้อมูลที่มีซ้ำกันในทุกเซต (intersection) ที่เราสนใจได้โดยการสร้างเซตใหม่ขึ้นมาดังตัวอย่างในบรรทัดที่ 28 และสำเนาข้อมูลจากเซตใดเซตหนึ่งดังตัวอย่างในบรรทัดที่ 29 จากนั้นใช้เมธอด retainAll() ในการสำเนาข้อมูลจากเซตอื่นแต่ละเซตที่ต้องการดังตัวอย่างในบรรทัดที่ 30 ผลลัพธ์ที่ได้คือเซตที่มีเฉพาะข้อมูลที่มีซ้ำกันในทุกเซตที่เราสนใจโดยดูได้จากข้อมูลที่พิมพ์ออกมา
เราสามารถลบสมาชิกของเซตที่เป็นสมาชิกในเซตอื่นได้โดยใช้เมธอด removeAll() และระบุเซตที่ต้องการอ้างอิงดังตัวอย่างในบรรทัดที่ 36 ผลลัพธ์ที่ได้คือเซตที่มีเฉพาะส่วนที่ยังเหลืออยู่หลังจากลบข้อมูลที่ต้องการออกแล้วโดยดูได้จากข้อมูลที่พิมพ์ออกมา
จากตัวอย่างจะเห็นว่าเซตที่เป็นผลลัพธ์จากการรวมข้อมูลหรือแยกเฉพาะข้อมูลที่มีซ้ำกันจะมีข้อมูลไม่เท่ากับเซตต้นฉบับดังนั้นหากเราใช้เซตต้นฉบับเพื่อเก็บผลลัพธ์จะทำให้เซตต้นฉบับถูกแก้ไข ดังนั้นหากเราต้องการดำเนินการใดๆที่เสี่ยงต่อการที่เซตต้นฉบับจะถูกแก้ไขให้สร้างเซตใหม่ขึ้นมาเสมอเพื่อไม่ให้เกิดผลกระทบกับเซตต้นฉบับ
โครงสร้างข้อมูลแบบ Map
คลาสแบบอินเตอร์เฟส Map เป็นโครงสร้างข้อมูลใน Collection Framework ที่แยกออกมาต่างหาก โดยมีรูปแบบการเก็บข้อมูลเป็นคู่ประกอบด้วยคีย์และข้อมูล โดยข้อมูลในโครงสร้างข้อมูลแบบ Map เดียวกันห้ามใช้คีย์ซ้ำกัน ภายใต้คลาสแบบอินเตอร์เฟส Map มีทั้งคลาส เช่น HashMap และคลาสอินเตอร์เฟสย่อย (sub-interface) เช่น SortedMap
ตัวอย่างด้านล่างเป็นการสร้างชุดข้อมูลแบบ HashMap โดยการบันทึกข้อมูลเข้าไปในโครงสร้างใช้เมธอด put() มีพารามิเตอร์แรกเป็นคีย์ ส่วนพารามิเตอร์ที่สองเป็นข้อมูลที่จับคู่กับคีย์ เช่น myMember.put(key1,”Somchai”) ถ้าดูจากบรรทัดที่ 17-18 ในการพิมพ์ข้อมูลเราใช้เมธอด get() และระบุพารามิเตอร์เป็นคีย์ที่ต้องการพิมพ์ข้อมูล เช่น myMember.get(key1) สังเกตุว่าเราใช้คีย์ในการเข้าถึงข้อมูลสำหรับชุดข้อมูลแบบ Map เพราะไม่มีดัชนีให้ใช้งานเหมือนกับชุดข้อมูลแบบ List
สำหรับการลบข้อมูลออกจากชุดข้อมูลใช้เมธอด remove() และระบุพารามิเตอร์เป็นคีย์ที่ต้องการลบข้อมูล เช่น myMember.remove(key1) หรือเราจะระบุข้อมูลไปด้วยเพื่อความชัดเจนว่าลบไม่ผิดตัวแน่ เช่น meMembe.remove(key1,”Somchai”) หากเราต้องการแทนที่ข้อมูลเดิมให้ใช้เมธอด replace() โดยระบุพารามิเตอร์เป็นคีย์ที่ต้องการแก้ไขข้อมูล เช่น myMember.replace(key1,”Santi”) เป็นต้น
HashMap เป็นชุดข้อมูลที่ไม่มีการเรียงลำดับดังนั้นลำดับการพิมพ์รายการข้อมูลจะเปลี่ยนไปมาได้และไม่อิงกับลำดับในการป้อนข้อมูลด้วย
เนื่องจากคีย์ห้ามซ้ำกัน หากเราใช้เมธอด put() โดยกำหนดคีย์ที่ซ้ำกันข้อมูลใหม่จะถูกนำไปแทนที่ข้อมูลเดิม เพื่อเป็นการป้องกันเราควรสร้างเงื่อนไขด้วย if เพื่อตรวจสอบก่อนว่ามีคีย์ที่จะเพิ่มอยู่แล้วหรือไม่
การโอวอร์ไรด์เมธอด equals และ hashCode
ก่อนอื่นเราจะทบทวนเรื่องการเก็บข้อมูลของออบเจกต์ซึ่งเป็นชนิดข้อมูลแบบ reference data type ดังนี้
จากรูปด้านบนจะเห็นว่าชนิดข้อมูลแบบ reference data type นั้นตัวแปรจะชี้ไปยังค่าอ้างอิงหรือที่อยู่ในหน่วยความจำซึ่งจะชี้ไปยังที่เก็บข้อมูลอีกทีหนึ่ง ดังนั้นถ้าเราจะบอกว่าออบเจต์ใดเท่ากันหมายถึงทั้งข้อมูลที่เก็บและค่าอ้างอิงจะต้องเท่ากัน จากตัวอย่างด้านล่าง เราสร้างคลาส MyClass ขึ้นมาโดยมีฟิลด์ที่เป็นข้อความ จากนั้นจึงสร้างออบเจกต์ name1 name2 และ name3 ขึ้นมาและเก็บข้อความเดียวกัน แต่ให้ name3 เท่ากับ name1 จากนั้นเราตรวจสอบว่าแต่ละออบเจกต์เท่ากันหรือไม่ซึ่งผลลัพธ์คือ name1 ไม่เท่ากับ name2 ทั้งๆที่เราก็เห็นว่าเก็บข้อความเดียวกัน ซึ่งหากเรามาดูที่ค่าอ้างอิงจะเห็นว่า name1 และ name2 มีค่าอ้างอิงที่ไม่เท่ากัน นั่นหมายถึงทั้ง name1 และname2 ชี้ไปที่คนละออบเจกต์กัน ดังนั้นถึงแม้ว่าข้อความที่เก็บจะเหมือนกันแต่ก็เป็นคนละออบเจกต์
เมธอด equals() และ hashCode() เป็นเมธอดของคลาส Object ซึ่งเป็นคลาสแรกสุดใน Java API ดังนั้นทุกคลาสใน Java API จะสืบทอดเมธอดนี้มาด้วย โดยเมธอด hashCode() จะแสดงค่า hash ของออบเจกต์ส่วนเมธอด equals() จะเปรียบเทียบค่าอ้างอิงของออบเจกต์เท่านั้น ดังตัวอย่างด้านบน
สำหรับโครงสร้างข้อมูล Map และ Set ซึ่งมีส่วนที่เป็นคีย์ที่ต้องซ้ำกันไม่ได้ แต่หากสมาชิกของชุดข้อมูลเป็นคลาสที่เราสร้างขึ้นมาเอง โอกาสที่สมาชิกของชุดข้อมูลจะซ้ำกันสามารถเกิดขึ้นได้เนื่องจากการตรวจสอบว่าสมาชิกซ้ำกันหรือไม่เป็นการใช้ความสามารถของเมธอด equals() และ hashCode() ซึ่ง
เฉพาะอัลกอริธึมของทั้งเมธอด equals() และ hashCode() ที่สืบทอดมาจากคลาส Object นั้นไม่เพียงพอต่อการใช้งานกับคลาสที่เราสร้างขึ้นมา เราจึงต้องโอเวอร์ไรด์ทั้ง 2 เมธอดให้เหมาะสมเพื่อให้ผลลัพธ์ของการใช้โครงสร้างข้อมูล Map และ Set นั้นถูกต้องคือไม่ยอมให้คีย์มีข้อมูลที่ซ้ำกัน
ในกรณีที่เราใช้งานคลาส String เป็นสมาชิกใน Map หรือ Set แล้วไม่พบปัญหานี้ก็เนื่องมาจากคลาส String มีการโอเวอร์ไรด์ทั้ง 2 เมธอดให้เหมาะสมกับข้อมูลแบบ String อยู่แล้วเราจึงสามารถใช้งานได้อย่างถูกต้อง ดังตัวอย่างด้านล่างเราสร้างชุดข้อมูล HashSet ขึ้นมาและพยายามเก็บค่า Test ซ้ำกันแต่ไม่สามารถทำได้โดยสังเกตุได้จากว่าเราพิมพ์ข้อมูลทั้งหมดในเซตแล้วมีค่า Test เพียงค่าเดียว
ทีนี้เราลองสร้างคลาส MyText ของเราเองดังตัวอย่างด้านล่างโดยมีฟิลด์เพื่อเก็บข้อความและสร้างออบเจต์ที่เก็บข้อความเดียวกันขึ้นมา 2 ออบเจกต์และกำหนดให้ทั้ง 2 ออบเจกต์เป็นสมาชิกในเซตเดียวกัน จะเห็นว่าเราสามารถเก็บข้อความที่ซ้ำกันได้โดยสังเกตได้จากว่าเราพิมพ์ข้อมูลทั้งหมดในเซตแล้วมีค่า Test ซ้ำกัน ทั้งนี้เพราะการซ้ำกันในความเข้าใจของเรากับความเข้าใจของภาษาจาวาซึ่งใช้เมธอด equals() และ hashCode() พื้นฐานนั้นไม่เหมือนกัน การมองว่าออบเจกต์ซ้ำกันของภาษาจาวาคือต้องซ้ำกันทั้งข้อมูลและค่าอ้างอิง
จะเห็นว่าคลาสอื่นๆใน Java API ที่ต้องการการตรวจสอบแบบนี้ เช่น คลาส String จึงต้องโอเวอร์ไรด์เมธอด equals() และ hashCode() ให้เหมาะสมกับตัวเอง ซึ่งเราก็ต้องทำเช่นเดียวกันกับคลาสของเรา โดยเราเองจะต้องกำหนดว่าการเท่ากันของเราคืออะไรและแฮชโค๊ดของเราจะได้มาอย่างไร ดังตัวอย่างด้านล่างเราออกแบบว่าการเท่ากันของเราคือเป็นข้อความเดียวกันเราจึงโอเวอร์ไรด์เมธอด equals() เพื่อเปรียบเทียบข้อความซึ่งเราเปรียบเทียบโดยใช้เมธอด equals() ของคลาส String และโอเวอร์ไรด์เมธอด hashCode() เพื่อหาค่าแฮชจากฟิลด์โดยบวกตัวเลขเพิ่มเข้าไปเพื่อให้แน่ใจว่าจะได้แฮชโค๊ดที่ไม่ซ้ำกันกับการแฮชข้อความ สังเกตุได้จากว่าเมื่อเราโอเวอร์ไรด์เมธอดให้เหมาะสมแล้วข้อมูลทั้งหมดในเซตที่พิมพ์ออกมามีค่า Test เพียงค่าเดียว