1. คำอธิบาย
สำหรับวิธีการสำรวจแผนที่ใน Java บทความจำนวนมากแนะนำให้ใช้ entrySet ซึ่งถือว่ามีประสิทธิภาพมากกว่าชุดคีย์มาก เหตุผลก็คือ: เมธอด entrySet รับชุดของคีย์และค่าทั้งหมดพร้อมกัน ในขณะที่ชุดคีย์รับเฉพาะชุดของคีย์ สำหรับแต่ละคีย์ จะต้องค้นหาค่าใน Map อีกครั้ง ซึ่งจะช่วยลด ประสิทธิภาพโดยรวม แล้วสถานการณ์จริงเป็นอย่างไร?
เพื่อทำความเข้าใจช่องว่างที่แท้จริงของประสิทธิภาพการข้ามผ่าน รวมถึงความแตกต่างในสถานการณ์ที่แตกต่างกัน เช่น คีย์+ค่า คีย์การข้าม ค่าการข้าม ฯลฯ ฉันพยายามดำเนินการทดสอบเปรียบเทียบบางอย่าง
2. การทดสอบเปรียบเทียบ
ในตอนแรกมีเพียงการทดสอบง่ายๆ แต่ผลลัพธ์แสดงให้เห็นว่าประสิทธิภาพของ keySet ดีกว่า ซึ่งทำให้ฉันงงงวยไม่ใช่หรือว่า entrySet ดีกว่า keySet อย่างเห็นได้ชัด เพื่อการตรวจสอบเพิ่มเติม ข้อมูลการทดสอบที่แตกต่างกันได้ถูกนำมาใช้สำหรับการทดสอบเปรียบเทียบที่มีรายละเอียดมากขึ้น
2.1 ข้อมูลการทดสอบ
2.1.1 ข้อมูลการทดสอบ HashMap
HashMap-1 ขนาด 1 ล้าน คีย์และค่าเป็นทั้ง String ค่าคีย์คือ 1, 2, 3...1000000:
คัดลอกรหัสรหัสดังต่อไปนี้:
แผนที่ <String, String> แผนที่ = HashMap ใหม่ <String, String>();
สตริงคีย์, ค่า;
สำหรับ (i = 1; i <= num; i++) {
คีย์ = "" + ฉัน;
ค่า = "ค่า";
map.put (คีย์, ค่า);
-
HashMap-2 ขนาด 1 ล้านคีย์และค่าเป็นทั้ง String ค่าคีย์คือ 50, 100, 150, 200,..., 50000000:
คัดลอกรหัสรหัสดังต่อไปนี้:
แผนที่ <String, String> แผนที่ = HashMap ใหม่ <String, String>();
สตริงคีย์, ค่า;
สำหรับ (i = 1; i <= num; i++) {
คีย์ = "" + (i * 50);
ค่า = "ค่า";
map.put (คีย์, ค่า);
-
2.1.2 ข้อมูลการทดสอบ TreeMap
TreeMap-1 ขนาด 1 ล้านคีย์และค่าเป็นทั้ง String ค่าคีย์คือ 1, 2, 3...1000000:
คัดลอกรหัสรหัสดังต่อไปนี้:
แผนที่ <String, String> map = TreeMap ใหม่ <String, String>();
สตริงคีย์, ค่า;
สำหรับ (i = 1; i <= num; i++) {
คีย์ = "" + ฉัน;
ค่า = "ค่า";
map.put (คีย์, ค่า);
-
TreeMap-2 ขนาด 1 ล้านคีย์และค่าเป็นทั้ง String ค่าคีย์คือ 50, 100, 150, 200,..., 50000000 แยกกันมากขึ้น:
คัดลอกรหัสรหัสดังต่อไปนี้:
แผนที่ <String, String> map = TreeMap ใหม่ <String, String>();
สตริงคีย์, ค่า;
สำหรับ (i = 1; i <= num; i++) {
คีย์ = "" + (i * 50);
ค่า = "ค่า";
map.put (คีย์, ค่า);
-
2.2 สถานการณ์การทดสอบ
ใช้วิธีการเขียนต่างๆ ของ keySet, entrySet และ Value เพื่อทดสอบสามสถานการณ์: คีย์+ค่าข้าม, คีย์ข้าม และค่าข้าม
2.2.1 คีย์การเคลื่อนที่+ค่า
ชุดคีย์ลัดลัดคีย์ + ค่า (วิธีการเขียน 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<สตริง> iter = map.keySet().ตัววนซ้ำ();
ในขณะที่ (iter.hasNext()) {
คีย์ = iter.next();
ค่า = map.get (คีย์);
-
keySet สำรวจคีย์ + ค่า (วิธีการเขียน 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (คีย์สตริง: map.keySet()) {
ค่า = map.get (คีย์);
-
entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<รายการ<สตริง, สตริง>> iter = map.entrySet().iterator();
รายการ <สตริง, สตริง> รายการ;
ในขณะที่ (iter.hasNext()) {
รายการ = iter.next();
คีย์ = entry.getKey();
ค่า = entry.getValue();
-
entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (รายการ <สตริง, สตริง> รายการ: map.entrySet()) {
คีย์ = entry.getKey();
ค่า = entry.getValue();
-
2.2.2 กุญแจหมุน
keySet traverses key (การเขียนวิธีที่ 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<สตริง> iter = map.keySet().ตัววนซ้ำ();
ในขณะที่ (iter.hasNext()) {
คีย์ = iter.next();
-
keySet traverses key (การเขียนวิธีที่ 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (คีย์สตริง: map.keySet()) {
-
entrySet คีย์ลัด (การเขียนวิธีที่ 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<รายการ<สตริง, สตริง>> iter = map.entrySet().iterator();
ในขณะที่ (iter.hasNext()) {
คีย์ = iter.next().getKey();
-
entrySet คีย์ลัด (การเขียนวิธีที่ 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (รายการ <สตริง, สตริง> รายการ: map.entrySet()) {
คีย์ = entry.getKey();
-
2.2.3 ค่าการเคลื่อนที่
ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<สตริง> iter = map.keySet().ตัววนซ้ำ();
ในขณะที่ (iter.hasNext()) {
ค่า = map.get(iter.next());
-
ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (คีย์สตริง: map.keySet()) {
ค่า = map.get (คีย์);
-
entrySet ข้ามค่า (วิธีการเขียน 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<รายการ<สตริง, สตริง>> iter = map.entrySet().iterator();
ในขณะที่ (iter.hasNext()) {
ค่า = iter.next().getValue();
-
entrySet ข้ามค่า (วิธีการเขียน 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (รายการ <สตริง, สตริง> รายการ: map.entrySet()) {
ค่า = entry.getValue();
-
ค่าลัดเลาะค่า (วิธีการเขียน 1):
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัววนซ้ำ<สตริง> iter = map.values().ตัววนซ้ำ();
ในขณะที่ (iter.hasNext()) {
ค่า = iter.next();
-
ค่าลัดเลาะค่า (วิธีการเขียน 2):
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (ค่าสตริง : map.values()) {
-
2.3 ผลการทดสอบ
2.3.1 ผลการทดสอบ HashMap
หน่วย: มิลลิวินาที | แฮชแมป-1 | แฮชแมป-2 |
| keySet สำรวจคีย์ + ค่า (วิธีการเขียน 1) | 39 | 93 |
| keySet สำรวจคีย์ + ค่า (วิธีการเขียน 2) | 38 | 87 |
| entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 1) | 43 | 86 |
| entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 2) | 43 | 85 |
หน่วย: มิลลิวินาที | แฮชแมป-1 | แฮชแมป-2 |
| keySet traverse key (การเขียนวิธีที่ 1) | 27 | 65 |
| keySet คีย์ลัด (วิธีการเขียน 2) | 26 | 64 |
| entrySet คีย์ลัด (วิธีการเขียน 1) | 35 | 75 |
| entrySet คีย์การสำรวจเส้นทาง (วิธีการเขียน 2) | 34 | 74 |
หน่วย: มิลลิวินาที | แฮชแมป-1 | แฮชแมป-2 |
| ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 1) | 38 | 87 |
| ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 2) | 37 | 87 |
| entrySet ค่าการสำรวจเส้นทาง (วิธีการเขียน 1) | 34 | 61 |
| entrySet ค่าการสำรวจเส้นทาง (วิธีการเขียน 2) | 32 | 62 |
| ค่าค่าการเคลื่อนที่ (วิธีเขียน 1) | 26 | 48 |
| ค่าค่าการเคลื่อนที่ (วิธีเขียน 2) | 26 | 48 |
2.3.2 ผลการทดสอบ TreeMap
หน่วย: มิลลิวินาที | ทรีแมป-1 | ทรีแมป-2 |
| ชุดคีย์ลัดลัดคีย์ + ค่า (วิธีการเขียน 1) | 430 | 451 |
| keySet สำรวจคีย์ + ค่า (วิธีการเขียน 2) | 429 | 450 |
| entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 1) | 77 | 84 |
| entrySet สำรวจคีย์ + ค่า (วิธีการเขียน 2) | 70 | 68 |
หน่วย: มิลลิวินาที | ทรีแมป-1 | ทรีแมป-2 |
| keySet traverses key (การเขียนวิธีที่ 1) | 50 | 49 |
| keySet คีย์ลัด (วิธีการเขียน 2) | 49 | 48 |
| entrySet คีย์ลัด (วิธีการเขียน 1) | 66 | 64 |
| entrySet คีย์การสำรวจเส้นทาง (วิธีการเขียน 2) | 65 | 63 |
หน่วย: มิลลิวินาที | ทรีแมป-1 | ทรีแมป-2 |
| ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 1) | 432 | 448 |
| ค่าการข้ามคีย์เซ็ต (วิธีการเขียน 2) | 430 | 448 |
| entrySet ค่าการสำรวจเส้นทาง (วิธีการเขียน 1) | 62 | 61 |
| entrySet ค่าการสำรวจเส้นทาง (วิธีการเขียน 2) | 62 | 61 |
| ค่าค่าการเคลื่อนที่ (วิธีเขียน 1) | 46 | 46 |
| ค่าค่าการเคลื่อนที่ (วิธีเขียน 2) | 45 | 46 |
3. บทสรุป
3.1 หากคุณใช้ HashMap
1. เมื่อสำรวจคีย์และค่าในเวลาเดียวกัน ความแตกต่างด้านประสิทธิภาพระหว่างวิธี keySet และ entrySet ขึ้นอยู่กับเงื่อนไขเฉพาะของคีย์ เช่น ความซับซ้อน (วัตถุที่ซับซ้อน) ความแตกต่าง อัตราความขัดแย้ง ฯลฯ กล่าวอีกนัยหนึ่ง ขึ้นอยู่กับค่าใช้จ่ายในการค้นหามูลค่าใน HashMap การดำเนินการของ entrySet เพื่อดึงคีย์และค่าทั้งหมดพร้อมกันมีค่าใช้จ่ายด้านประสิทธิภาพ เมื่อการสูญเสียนี้น้อยกว่าค่าใช้จ่ายของ HashMap ที่กำลังมองหาค่า ข้อได้เปรียบด้านประสิทธิภาพของ entrySet จะสะท้อนให้เห็น ตัวอย่างเช่น ในการทดสอบเปรียบเทียบข้างต้น เมื่อคีย์เป็นสตริงตัวเลขที่ง่ายที่สุด ชุดคีย์อาจมีประสิทธิภาพมากกว่า โดยใช้เวลาน้อยกว่าชุดรายการ 10% โดยทั่วไป ขอแนะนำให้ใช้ entrySet เนื่องจากเมื่อคีย์นั้นเรียบง่ายมาก ประสิทธิภาพของคีย์อาจต่ำกว่าชุดคีย์เล็กน้อย แต่สามารถควบคุมได้ เนื่องจากคีย์มีความซับซ้อนมากขึ้น ข้อดีของ entrySet จะถูกสะท้อนให้เห็นอย่างชัดเจน แน่นอนว่าเราสามารถเลือกได้ตามสถานการณ์จริง
2. เมื่อเฉพาะคีย์ลัด วิธีการ keySet จะเหมาะสมกว่า เนื่องจาก entrySet จะนำค่าที่ไม่มีประโยชน์ออกไปด้วย ซึ่งจะทำให้ประสิทธิภาพและพื้นที่สิ้นเปลือง จากผลการทดสอบข้างต้น keySet ใช้เวลาน้อยกว่าวิธี entrySet ถึง 23%
3. เมื่อเฉพาะค่าการสำรวจ การใช้วิธี vlaues เป็นตัวเลือกที่ดีที่สุด entrySet จะดีกว่าวิธี keySet เล็กน้อย
4. ขอแนะนำให้ใช้วิธีการเขียนต่อไปนี้ซึ่งมีประสิทธิภาพมากกว่าเล็กน้อยในวิธีการเขียนการสำรวจเส้นทางต่างๆ:
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (คีย์สตริง: map.keySet()) {
ค่า = map.get (คีย์);
-
สำหรับ (รายการ <สตริง, สตริง> รายการ: map.entrySet()) {
คีย์ = entry.getKey();
ค่า = entry.getValue();
-
สำหรับ (ค่าสตริง : map.values()) {
-
3.2 หากคุณใช้ TreeMap
1. เมื่อสำรวจคีย์และค่าพร้อมกัน ซึ่งแตกต่างจาก HashMap ประสิทธิภาพของ entrySet จะสูงกว่าชุดคีย์มาก สิ่งนี้ถูกกำหนดโดยประสิทธิภาพการสืบค้นของ TreeMap กล่าวอีกนัยหนึ่งค่าใช้จ่ายในการค้นหาค่าใน TreeMap ค่อนข้างมากซึ่งสูงกว่าต้นทุนในการดึงคีย์และค่าทั้งหมดพร้อมกันจาก entrySet ดังนั้นจึงขอแนะนำเป็นอย่างยิ่งให้ใช้วิธี entrySet เมื่อสำรวจ TreeMap
2. เมื่อเฉพาะคีย์ลัด วิธีการ keySet จะเหมาะสมกว่า เนื่องจาก entrySet จะนำค่าที่ไม่มีประโยชน์ออกไปด้วย ซึ่งจะทำให้ประสิทธิภาพและพื้นที่สิ้นเปลือง จากผลการทดสอบข้างต้น keySet ใช้เวลาน้อยกว่าวิธี entrySet ถึง 24%
3. เมื่อเฉพาะค่าข้ามผ่าน การใช้วิธี vlaues เป็นตัวเลือกที่ดีที่สุด และ entrySet ก็ดีกว่าวิธี keySet อย่างเห็นได้ชัด
4. ขอแนะนำให้ใช้วิธีการเขียนต่อไปนี้ซึ่งมีประสิทธิภาพมากกว่าเล็กน้อยในวิธีการเขียนการสำรวจเส้นทางต่างๆ:
คัดลอกรหัสรหัสดังต่อไปนี้:
สำหรับ (คีย์สตริง: map.keySet()) {
ค่า = map.get (คีย์);
-
สำหรับ (รายการ <สตริง, สตริง> รายการ: map.entrySet()) {
คีย์ = entry.getKey();
ค่า = entry.getValue();
-
สำหรับ (ค่าสตริง : map.values()) {
-