มีข้อดีของการใช้แผนที่บน unordered_map ในกรณีที่เป็นกุญแจสำคัญหรือไม่?


371

การพูดคุยเมื่อไม่นานมานี้unordered_mapใน C ++ ทำให้ฉันรู้ว่าฉันควรใช้unordered_mapกับกรณีส่วนใหญ่ที่ฉันเคยใช้มาmapก่อนเนื่องจากประสิทธิภาพของการค้นหา ( ตัดจำหน่าย O (1)เทียบกับO (log n ) เวลาส่วนใหญ่ที่ฉันใช้แผนที่ฉันใช้อย่างใดอย่างหนึ่งintหรือstd::stringเป็นชนิดคีย์ ดังนั้นฉันไม่มีปัญหากับนิยามของฟังก์ชันแฮช ยิ่งฉันคิดถึงมันมากเท่าไรฉันก็ยิ่งรู้ว่าฉันไม่สามารถหาเหตุผลในการใช้ปุ่มstd::mapa std::unordered_mapในกรณีของปุ่มที่มีประเภทง่าย ๆ ได้ - ฉันดูที่อินเตอร์เฟสและไม่พบสิ่งใดเลย ความแตกต่างที่สำคัญที่จะส่งผลกระทบต่อรหัสของฉัน

ดังนั้นคำถาม: มีเหตุผลที่แท้จริงที่จะใช้std::mapมากกว่าstd::unordered_mapในกรณีของประเภทง่ายเช่นintและstd::string?

ฉันขอจากมุมมองการเขียนโปรแกรมอย่างเคร่งครัด - ฉันรู้ว่ามันไม่ได้พิจารณาอย่างเต็มที่มาตรฐานและอาจก่อให้เกิดปัญหากับการย้าย

นอกจากนี้ฉันคาดหวังว่าหนึ่งในคำตอบที่ถูกต้องอาจเป็น"มันมีประสิทธิภาพมากกว่าสำหรับชุดข้อมูลขนาดเล็ก"เนื่องจากมีค่าใช้จ่ายน้อย (นั่นเป็นเรื่องจริงหรือ?) - ดังนั้นฉันจึงต้องการ จำกัด คำถามให้กับกรณีที่จำนวน คีย์นั้นไม่สำคัญ (> 1 024)

แก้ไข: duh ฉันลืมชัดเจน (ขอบคุณ GMan!) - ใช่แผนที่ได้รับคำสั่งแน่นอน - ฉันรู้แล้วและกำลังมองหาเหตุผลอื่น


22
ฉันชอบถามคำถามนี้ในการสัมภาษณ์: "เมื่อใดที่การเรียงลำดับแบบเร็วจะดีกว่าการเรียงลำดับแบบฟอง" คำตอบของคำถามให้ข้อมูลเชิงลึกเกี่ยวกับการใช้งานจริงของทฤษฎีความซับซ้อนและไม่เพียง แต่ข้อความธรรมดาสีดำและสีขาวเช่น O (1) จะดีกว่า O (n) หรือ O (k) เทียบเท่ากับ O (logn) ฯลฯ ..

42
@Beh ฉันคิดว่าคุณหมายถึง "เมื่อมีการเรียงลำดับฟองดีกว่าการจัดเรียงด่วน": P
Kornel Kisielewicz

2
ตัวชี้สมาร์ทจะเป็นกุญแจสำคัญหรือไม่?
Thomthom

นี่คือหนึ่งในกรณีที่แผนที่มีประโยชน์: stackoverflow.com/questions/51964419/…
anilbey

คำตอบ:


399

อย่าลืมว่าmapสั่งให้องค์ประกอบของมัน หากคุณไม่สามารถยอมแพ้ได้แน่นอนคุณไม่สามารถใช้งานunordered_mapได้

สิ่งอื่นที่ควรทราบก็คือunordered_mapโดยทั่วไปจะใช้หน่วยความจำมากขึ้น mapเพิ่งมีพอยน์เตอร์รักษาบ้านน้อยและหน่วยความจำสำหรับแต่ละวัตถุ ตรงกันข้ามunordered_mapมีอาเรย์ขนาดใหญ่ (สิ่งเหล่านี้อาจมีขนาดค่อนข้างใหญ่ในการใช้งานบางอย่าง) จากนั้นหน่วยความจำเพิ่มเติมสำหรับแต่ละวัตถุ หากคุณจำเป็นต้องตระหนักถึงหน่วยความจำmapควรพิสูจน์ได้ดีกว่าเพราะมันไม่มีอาร์เรย์ขนาดใหญ่

ดังนั้นหากคุณต้องการค้นหาการค้นหาอย่างแท้จริงฉันจะบอกว่าunordered_mapเป็นวิธีที่จะไป แต่ก็มีการแลกเปลี่ยนกันอยู่เสมอและหากคุณไม่สามารถซื้อได้คุณก็ไม่สามารถใช้งานได้

เพียงแค่จากประสบการณ์ส่วนตัวฉันพบว่าการปรับปรุงประสิทธิภาพมหาศาล (วัดแน่นอน) เมื่อใช้unordered_mapแทนการใช้mapในตารางค้นหาเอนทิตีหลัก

ในทางกลับกันฉันพบว่ามันช้าลงมากในการใส่และลบองค์ประกอบซ้ำ ๆ มันยอดเยี่ยมสำหรับคอลเลกชันที่ค่อนข้างคงที่ขององค์ประกอบ แต่ถ้าคุณทำการแทรกและการลบจำนวนมากดูเหมือนว่าการเพิ่มการแฮ็ช + การเพิ่มจำนวนมากขึ้น (หมายเหตุนี่เป็นการทำซ้ำหลายครั้ง)


3
สิ่งหนึ่งที่เพิ่มเติมเกี่ยวกับคุณสมบัติบล็อกหน่วยความจำขนาดใหญ่ของ unordered_map เทียบกับแผนที่ (หรือรายการเวกเตอร์เทียบกับ) กระบวนการฮีปเริ่มต้น (พูดถึง Windows ที่นี่) เป็นอนุกรม การจัดสรรบล็อก (เล็ก) ในปริมาณมากในแอปพลิเคชันแบบมัลติเธรดนั้นมีราคาแพงมาก
คำราม

4
RA: คุณสามารถควบคุมได้ด้วยประเภทการจัดสรรของคุณเองรวมกับคอนเทนเนอร์ใด ๆ หากคุณคิดว่าเป็นเรื่องสำคัญสำหรับโปรแกรมเฉพาะ

9
หากคุณรู้ขนาดของunordered_mapและสำรองที่เริ่มต้น - คุณยังคงจ่ายค่าปรับสำหรับการแทรกจำนวนมากหรือไม่? สมมติว่าคุณกำลังแทรกเพียงครั้งเดียวเมื่อคุณสร้างตารางการค้นหา - แล้วอ่านจากในภายหลังเท่านั้น
Thomthom

3
@thomthom เท่าที่ฉันสามารถบอกได้ว่าไม่ควรมีบทลงโทษในแง่ของประสิทธิภาพ เหตุผลที่ทำให้ผลงานออกมาดีเพราะความจริงที่ว่าถ้าอาร์เรย์มีขนาดใหญ่เกินไปมันจะทำการปรับปรุงองค์ประกอบทั้งหมด ถ้าคุณโทรสำรองมันอาจจะปรับปรุงองค์ประกอบที่มีอยู่ แต่ถ้าคุณเรียกมันว่าตอนเริ่มต้นก็ไม่ควรมีการลงโทษอย่างน้อยตามcplusplus.com/reference/unordered_map/unordered_map/reserve
Richard Fung

6
ฉันค่อนข้างแน่ใจว่าหน่วยความจำที่ชาญฉลาดมันเป็นสิ่งที่ตรงกันข้าม สมมติว่าค่าโหลดเริ่มต้น 1.0 สำหรับคอนเทนเนอร์ที่ไม่มีการเรียงลำดับ: คุณมีตัวชี้หนึ่งตัวต่อองค์ประกอบสำหรับที่เก็บข้อมูลและหนึ่งตัวชี้ต่อองค์ประกอบสำหรับองค์ประกอบถัดไปในที่เก็บข้อมูลดังนั้นคุณจึงสิ้นสุดด้วยตัวชี้สองตัวพร้อมข้อมูลต่อแต่ละองค์ประกอบ สำหรับคอนเทนเนอร์ที่สั่งซื้อในทางกลับกันการใช้งาน RB-tree ทั่วไปจะมี: พอยน์เตอร์สามตัว (ซ้าย / ขวา / พาเรนต์) บวกบิตสีซึ่งเนื่องจากการจัดตำแหน่งใช้คำถัดไป นั่นคือตัวชี้สี่ตัวบวกข้อมูลต่อแต่ละองค์ประกอบ
Yakov Galka

126

หากคุณต้องการเปรียบเทียบความเร็วของการใช้งานstd::mapและstd::unordered_mapการใช้งานของคุณคุณสามารถใช้โครงการsparsehashของ Google ซึ่งมีโปรแกรม time_hash_map เพื่อกำหนดเวลา ตัวอย่างเช่นด้วย gcc 4.4.2 บนระบบ x86_64 Linux

$ ./time_hash_map
TR1 UNORDERED_MAP (4 byte objects, 10000000 iterations):
map_grow              126.1 ns  (27427396 hashes, 40000000 copies)  290.9 MB
map_predict/grow       67.4 ns  (10000000 hashes, 40000000 copies)  232.8 MB
map_replace            22.3 ns  (37427396 hashes, 40000000 copies)
map_fetch              16.3 ns  (37427396 hashes, 40000000 copies)
map_fetch_empty         9.8 ns  (10000000 hashes,        0 copies)
map_remove             49.1 ns  (37427396 hashes, 40000000 copies)
map_toggle             86.1 ns  (20000000 hashes, 40000000 copies)

STANDARD MAP (4 byte objects, 10000000 iterations):
map_grow              225.3 ns  (       0 hashes, 20000000 copies)  462.4 MB
map_predict/grow      225.1 ns  (       0 hashes, 20000000 copies)  462.6 MB
map_replace           151.2 ns  (       0 hashes, 20000000 copies)
map_fetch             156.0 ns  (       0 hashes, 20000000 copies)
map_fetch_empty         1.4 ns  (       0 hashes,        0 copies)
map_remove            141.0 ns  (       0 hashes, 20000000 copies)
map_toggle             67.3 ns  (       0 hashes, 20000000 copies)

2
ดูเหมือนว่าแผนที่ที่ไม่มีการเรียงลำดับจะเป็นแผนที่ของการดำเนินงานส่วนใหญ่กิจกรรมในการแทรก ...
Michael IV 4

7
sparsehash ไม่มีอยู่อีกต่อไป มันถูกลบหรือถอดออก
User9102d82

1
@ User9102d82 ผมได้แก้ไขคำถามเพื่ออ้างถึงการเชื่อมโยง waybackmachine
andreee

เพียงเพื่อให้แน่ใจว่าคนอื่น ๆ สังเกตเห็นตัวเลขอื่น ๆ นอกเหนือจากเวลาเช่นกัน: การทดสอบเหล่านั้นทำด้วยวัตถุ / ไบต์ข้อมูล 4 ไบต์หรือที่รู้จักกันในชื่อ int หากคุณจัดเก็บบางสิ่งที่จำเป็นต้องมีการแฮ็กหนักหรือมีขนาดใหญ่กว่า (ทำให้การถ่ายสำเนาหนักกว่า) แผนที่มาตรฐานอาจมีข้อได้เปรียบอย่างรวดเร็ว!
AlexGeorg

82

ฉันจะพูดอย่างคร่าวๆถึงจุดเดียวกับที่ GMan ทำขึ้น: ขึ้นอยู่กับประเภทการใช้งานstd::mapอาจเป็น (และบ่อยครั้ง) เร็วกว่าstd::tr1::unordered_map(ใช้การใช้งานที่รวมอยู่ใน VS 2008 SP1)

มีปัจจัยแทรกซ้อนบางอย่างที่ต้องจำไว้ ตัวอย่างเช่นในstd::mapคุณกำลังเปรียบเทียบคีย์ซึ่งหมายความว่าคุณดูที่จุดเริ่มต้นของคีย์เพียงพอที่จะแยกความแตกต่างระหว่างสาขาย่อยด้านขวาและซ้ายของต้นไม้เท่านั้น จากประสบการณ์ของฉันเกือบทุกครั้งที่คุณดูคีย์ทั้งหมดคือถ้าคุณใช้บางสิ่งบางอย่างเช่น int ที่คุณสามารถเปรียบเทียบได้ในคำสั่งเดียว ด้วยประเภทคีย์ทั่วไปเช่นสตริง std :: คุณมักจะเปรียบเทียบเพียงไม่กี่ตัวอักษรหรือมากกว่านั้น

ฟังก์ชั่นแฮชที่เหมาะสมตรงกันข้ามจะมองที่คีย์ทั้งหมด IOW แม้ว่าการค้นหาตารางจะมีความซับซ้อนคงที่แฮชตัวเองมีความซับซ้อนเชิงเส้นคร่าวๆ (แม้ว่าจะอยู่บนความยาวของคีย์ไม่ใช่จำนวนรายการ) ด้วยสตริงที่ยาวเป็นคีย์การstd::mapค้นหาอาจเสร็จสิ้นก่อนที่unordered_mapจะเริ่มต้นการค้นหา

ประการที่สองในขณะที่มีหลายวิธีในการปรับขนาดตารางกัญชาที่สุดของพวกเขาจะสวยช้า - ไปยังจุดที่ว่าถ้าการค้นหาเป็นอย่างมากมากขึ้นบ่อยกว่าการแทรกและลบ Std :: std::unordered_mapแผนที่มักจะเร็วกว่า

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

อีกประเด็นหนึ่ง: ข้อกำหนดสำหรับการแปลงแป้นพิมพ์และแผนที่ที่ใช้ต้นไม้นั้นแตกต่างกัน เห็นได้ชัดว่าการแฮชต้องใช้ฟังก์ชันแฮชและการเปรียบเทียบความเท่าเทียมกันซึ่งแผนที่ที่สั่งซื้อต้องมีการเปรียบเทียบน้อยกว่า แน่นอนไฮบริดที่ฉันพูดถึงต้องการทั้งสองอย่าง แน่นอนว่าสำหรับกรณีทั่วไปของการใช้สตริงเป็นคีย์นี่ไม่ใช่ปัญหาจริงๆ แต่มีบางประเภทของแป้นที่เหมาะกับการสั่งซื้อที่ดีกว่าการแปลงแป้นพิมพ์ (หรือกลับกัน)


2
การปรับขนาดแฮชสามารถลดลงได้โดยdynamic hashingเทคนิคซึ่งประกอบด้วยการมีช่วงการเปลี่ยนภาพซึ่งแต่ละครั้งที่คุณแทรกไอเท็kม แน่นอนหมายความว่าในช่วงการเปลี่ยนภาพคุณต้องค้นหา 2 ตารางที่แตกต่างกัน ...
Matthieu M.

2
"ด้วยสตริงที่มีความยาวเป็นคีย์ std :: map อาจทำการค้นหาให้เสร็จสิ้นก่อนที่ unordered_map จะเริ่มต้นการค้นหา" - หากไม่มีรหัสในคอลเลกชัน หากเป็นปัจจุบันแน่นอนว่าต้องเปรียบเทียบความยาวทั้งหมดเพื่อยืนยันการแข่งขัน แต่ในทำนองเดียวกันunordered_mapต้องยืนยันการจับคู่แฮชด้วยการเปรียบเทียบแบบสมบูรณ์ดังนั้นทุกอย่างขึ้นอยู่กับส่วนของกระบวนการค้นหาที่คุณตัดกัน
Steve Jessop

2
คุณสามารถแทนที่ฟังก์ชันแฮชตามความรู้ของข้อมูลได้ ตัวอย่างเช่นหากสตริงยาวของคุณมีความหลากหลายมากขึ้นในช่วง 20 ไบต์สุดท้ายในช่วง 100 ปีแรกเพียงแค่แฮช 20 ไฟล์สุดท้าย
Erik Aronesty

56

ฉันรู้สึกทึ่งกับคำตอบจาก @Jerry Coffin ซึ่งแนะนำว่าแผนที่ที่สั่งจะแสดงประสิทธิภาพที่เพิ่มขึ้นในสตริงยาวหลังจากการทดลองบางอย่าง (ซึ่งสามารถดาวน์โหลดได้จากpastebin ) ฉันพบว่าสิ่งนี้ดูเหมือนจะเป็นจริงสำหรับคอลเลกชันเท่านั้น ของสตริงสุ่มเมื่อแผนที่เริ่มต้นด้วยพจนานุกรมเรียง (ซึ่งมีคำที่มีจำนวนมากของคำนำหน้า - ทับซ้อนกัน) กฎนี้แตกลงสันนิษฐานว่าน่าจะเป็นเพราะความลึกของต้นไม้ที่เพิ่มขึ้นจำเป็นในการดึงค่า ผลลัพธ์จะแสดงด้านล่างคอลัมน์หมายเลข 1 คือเวลาแทรกส่วนที่สองคือเวลาดึงข้อมูล

g++ -g -O3 --std=c++0x   -c -o stdtests.o stdtests.cpp
g++ -o stdtests stdtests.o
gmurphy@interloper:HashTests$ ./stdtests
# 1st number column is insert time, 2nd is fetch time
 ** Integer Keys ** 
 unordered:      137      15
   ordered:      168      81
 ** Random String Keys ** 
 unordered:       55      50
   ordered:       33      31
 ** Real Words Keys ** 
 unordered:      278      76
   ordered:      516     298

2
ขอบคุณสำหรับการทดสอบ เพื่อให้แน่ใจว่าเราไม่ได้ทำการวัดสัญญาณรบกวนฉันได้ทำการเปลี่ยนการทำงานแต่ละครั้งหลายครั้ง (และใส่ตัวนับแทน 1 ลงในแผนที่) ฉันวิ่งไปตามจำนวนแป้นที่แตกต่างกัน (จาก 2 ถึง 1,000) และมากถึง ~ 100 ปุ่มในแผนที่ซึ่งstd::mapโดยปกติแล้วจะมีประสิทธิภาพสูงกว่าstd::unordered_mapโดยเฉพาะอย่างยิ่งสำหรับปุ่มจำนวนเต็ม แต่ ~ 100 ปุ่มดูเหมือนว่ามันจะสูญเสียขอบและstd::unordered_mapเริ่มชนะ การแทรกลำดับที่สั่งไว้แล้วลงใน a std::mapแย่มากคุณจะได้รับสถานการณ์กรณีที่เลวร้ายที่สุด (O (N))
Andreas Magnusson

30

ฉันจะชี้ให้เห็นว่า ... มีหลายชนิดของunordered_maps

ค้นหาบทความ Wikipediaบนแผนที่แฮช ขึ้นอยู่กับว่ามีการนำไปใช้งานแบบใดลักษณะของการค้นหาการแทรกและการลบอาจแตกต่างกันอย่างมีนัยสำคัญ

และนั่นคือสิ่งที่ทำให้ฉันกังวลมากที่สุดด้วยการเพิ่มunordered_mapSTL: พวกเขาจะต้องเลือกการใช้งานเฉพาะที่ฉันสงสัยว่าพวกเขาจะลงไปตามPolicyถนนและดังนั้นเราจะติดอยู่กับการใช้งานสำหรับการใช้งานเฉลี่ยและไม่มีอะไรสำหรับ กรณีอื่น ๆ ...

ตัวอย่างเช่นแผนที่แฮชบางแห่งมีการทำแผนที่แบบเชิงเส้นตรงซึ่งแทนที่จะทำการทำแผนที่แฮชทั้งหมดในคราวเดียวส่วนหนึ่งจะถูกทำใหม่ในแต่ละการแทรกซึ่งจะช่วยลดต้นทุน

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

ดังนั้นในขณะนี้ฉันมักจะชอบstd::mapหรืออาจloki::AssocVector(สำหรับชุดข้อมูลแช่แข็ง)

อย่าเข้าใจฉันผิดฉันต้องการใช้std::unordered_mapและฉันอาจจะใช้ในอนาคต แต่มันยากที่จะ "เชื่อ" การพกพาของภาชนะดังกล่าวเมื่อคุณคิดถึงวิธีการนำไปใช้และการแสดงที่หลากหลาย ของสิ่งนี้


17
+1: จุดที่ถูกต้อง - ชีวิตง่ายขึ้นเมื่อฉันใช้การปรับใช้ของตัวเอง - อย่างน้อยฉันก็รู้ว่ามันถูกดูดที่ไหน :>
Kornel Kisielewicz

25

ความแตกต่างที่สำคัญที่ยังไม่ได้กล่าวถึงเพียงพอที่นี่:

  • mapทำให้ตัววนซ้ำไปยังองค์ประกอบทั้งหมดมีความเสถียรใน C ++ 17 คุณยังสามารถย้ายองค์ประกอบจากหนึ่งmapไปอีกองค์ประกอบหนึ่งโดยไม่ทำให้ตัววนซ้ำถูกต้องไปยังองค์ประกอบเหล่านั้น (และหากนำไปใช้อย่างเหมาะสมโดยไม่มีการจัดสรรใด ๆ
  • map โดยทั่วไปแล้วการกำหนดเวลาสำหรับการปฏิบัติการเดี่ยวจะมีความสอดคล้องกันมากกว่าเนื่องจากไม่ต้องการการจัดสรรจำนวนมาก
  • unordered_mapการใช้std::hashตามที่ดำเนินการใน libstdc ++ นั้นมีความเสี่ยงต่อ DoS หากป้อนด้วยอินพุตที่ไม่น่าเชื่อถือ (ใช้ MurmurHash2 กับเมล็ดคงที่ - ไม่ใช่ว่าการเพาะจะช่วยได้จริงๆดูhttps://emboss.github.io/blog/2012/12/14/ ทำลาย -hammmur-hash-flooding-dos-reloaded / )
  • การสั่งซื้อช่วยให้ค้นหาได้อย่างมีประสิทธิภาพเช่นวนซ้ำองค์ประกอบทั้งหมดด้วยคีย์≥ 42

14

ตารางแฮชมีค่าคงที่สูงกว่าการใช้แผนที่ทั่วไปซึ่งมีความสำคัญสำหรับคอนเทนเนอร์ขนาดเล็ก ขนาดสูงสุดคือ 10, 100 หรืออาจเท่ากับ 1,000 หรือมากกว่านั้น ค่าคงที่เหมือนเดิม แต่ O (บันทึก n) อยู่ใกล้กับ O (k) (โปรดจำไว้ว่าความซับซ้อนเชิงลอการิทึมยังคงดีจริงๆ )

ฟังก์ชันแฮชที่ดีขึ้นอยู่กับคุณสมบัติของข้อมูลของคุณ ดังนั้นถ้าฉันไม่ได้วางแผนที่จะดูฟังก์ชันแฮชที่กำหนดเอง (แต่สามารถเปลี่ยนใจได้ในภายหลังและอย่างง่ายดายเนื่องจากฉันพิมพ์คำด่าใกล้ทุกอย่าง) และแม้ว่าค่าเริ่มต้นจะถูกเลือกให้ทำงานได้อย่างเหมาะสมสำหรับแหล่งข้อมูลหลายแห่ง ธรรมชาติของแผนที่จะเพียงพอสำหรับความช่วยเหลือในตอนแรกที่ฉันยังคงเป็นค่าเริ่มต้นในการแมปมากกว่าตารางแฮชในกรณีนั้น

รวมถึงวิธีที่คุณไม่ต้องคิดเกี่ยวกับการเขียนฟังก์ชันแฮชสำหรับประเภทอื่น ๆ (โดยปกติคือ UDT) และเพียงแค่เขียน op <(ซึ่งคุณต้องการอยู่แล้ว)


@Roger คุณรู้จำนวนองค์ประกอบโดยประมาณที่ unordered_map แผนที่ดีที่สุดหรือไม่? ฉันอาจจะเขียนทดสอบแม้ว่า ... (+1)
Kornel Kisielewicz

1
@Kornel: มันไม่มากนักหรอก การทดสอบของฉันมีองค์ประกอบประมาณ 10,000 รายการ หากเราต้องการกราฟที่แม่นยำจริง ๆคุณสามารถดูการใช้งานmapและหนึ่งในunordered_mapนั้นด้วยแพลตฟอร์มและขนาดแคชที่แน่นอนและทำการวิเคราะห์ที่ซับซ้อน : P
GManNickG

ขึ้นอยู่กับรายละเอียดการใช้งานพารามิเตอร์การปรับเวลาคอมไพล์ (ง่ายต่อการสนับสนุนหากคุณกำลังเขียนการใช้งานของคุณเอง) และแม้แต่เครื่องเฉพาะที่ใช้สำหรับการทดสอบ เช่นเดียวกับตู้คอนเทนเนอร์อื่น ๆ คณะกรรมการจะกำหนดข้อกำหนดกว้าง ๆ เท่านั้น

13

เหตุผลที่ได้รับในคำตอบอื่น ๆ ; นี่คืออีก

std :: map (ต้นไม้ไบนารีสมดุล) การดำเนินการตัดจำหน่าย O (log n) และกรณีที่เลวร้ายที่สุด O (log n) std :: การดำเนินการ unordered_map (ตารางแฮช) ถูกตัดจำหน่าย O (1) และกรณีที่เลวร้ายที่สุด O (n)

วิธีปฏิบัติในเรื่องนี้ก็คือโต๊ะแฮช "hiccups" ทุกครั้งในขณะที่มีการดำเนินการ O (n) ซึ่งอาจเป็นหรือไม่ใช่สิ่งที่แอปพลิเคชันของคุณสามารถทนได้ หากไม่สามารถทนได้คุณควรเลือก std :: map over std :: unordered_map


12

สรุป

สมมติว่าการสั่งซื้อไม่สำคัญ:

  • หากคุณกำลังจะสร้างตารางขนาดใหญ่หนึ่งครั้งและทำแบบสอบถามจำนวนมากให้ใช้ std::unordered_map
  • หากคุณกำลังจะสร้างโต๊ะเล็ก (อาจอยู่ภายใต้องค์ประกอบ 100) std::mapและทำจำนวนมากของคำสั่งการใช้งาน O(log n)นี้เป็นเพราะอ่านที่มันเป็น
  • หากคุณกำลังจะเปลี่ยนโต๊ะบ่อยๆอาจ std::mapเป็นตัวเลือกที่ดี
  • std::unordered_mapหากคุณมีข้อสงสัยเพียงแค่ใช้

บริบททางประวัติศาสตร์

ในภาษาส่วนใหญ่แผนที่ที่ไม่ได้เรียงลำดับ (พจนานุกรมที่ใช้แฮช) เป็นแผนที่เริ่มต้นอย่างไรก็ตามใน C ++ คุณจะได้รับคำสั่งแผนที่เป็นแผนที่เริ่มต้น มันเกิดขึ้นได้อย่างไร? บางคนคิดผิดว่าคณะกรรมการ C ++ ตัดสินใจเรื่องนี้ด้วยภูมิปัญญาที่ไม่เหมือนใครของพวกเขา แต่ความจริงก็น่าเกลียดกว่านั้น

เป็นที่เชื่อกันอย่างกว้างขวางว่า C ++ จบลงด้วยแผนที่สั่งเป็นค่าเริ่มต้นเนื่องจากมีพารามิเตอร์ไม่มากเกินไปเกี่ยวกับวิธีการนำไปใช้ ในทางกลับกันการใช้งานแบบแฮชมีสิ่งต่าง ๆ มากมายให้พูดคุย ดังนั้นเพื่อหลีกเลี่ยง gridlocks ในมาตรฐานพวกเขาเพิ่งได้รับพร้อมกับแผนที่สั่ง รอบปี 2005 หลายภาษาแล้วมีการใช้งานที่ดีของการดำเนินการตามกัญชาและอื่น ๆ std::unordered_mapมันก็มากขึ้นง่ายขึ้นสำหรับคณะกรรมการที่จะยอมรับใหม่ ในโลกที่สมบูรณ์แบบstd::mapจะไม่มีการจัดลำดับและเราจะstd::ordered_mapแยกประเภทกัน

ประสิทธิภาพ

กราฟด้านล่างควรพูดด้วยตนเอง (ที่มา )

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


ข้อมูลที่น่าสนใจ คุณรวมแพลตฟอร์มจำนวนเท่าใดในการทดสอบ
Toby Speight

1
เหตุใดฉันจึงควรใช้ std :: map สำหรับตารางเล็ก ๆ เมื่อทำการสืบค้นจำนวนมากเนื่องจาก std :: unordered_map มักจะทำงานได้ดีกว่า std :: map ตาม 2 ภาพที่คุณโพสต์ที่นี่เสมอ
ricky

กราฟแสดงประสิทธิภาพสำหรับองค์ประกอบ 0.13M หรือมากกว่า หากคุณมีองค์ประกอบขนาดเล็ก (อาจเป็น <100) ดังนั้น O (บันทึก n) อาจเล็กกว่าแผนที่ที่ไม่ได้เรียงลำดับ
Shital Shah

10

ฉันได้ทำการทดสอบเมื่อเร็ว ๆ นี้ซึ่งทำให้มีการรวม & เรียง 50,000 นั่นหมายความว่าหากคีย์สตริงเหมือนกันให้รวมสตริงไบต์ และผลลัพธ์สุดท้ายควรจะเรียงลำดับ ดังนั้นนี่รวมถึงการค้นหาทุกการแทรก

สำหรับmapการนำไปใช้งานนั้นใช้เวลา 200 ms ในการทำให้งานเสร็จ สำหรับunordered_map+ mapจะใช้เวลา 70 ms สำหรับการunordered_mapแทรกและ 80 ms สำหรับการmapแทรก ดังนั้นการนำลูกผสมมาใช้จะรวดเร็วกว่า 50 ms

mapเราควรคิดว่าสองครั้งก่อนที่เราจะใช้ หากคุณต้องการข้อมูลที่จะจัดเรียงในผลลัพธ์สุดท้ายของโปรแกรมโซลูชันแบบผสมอาจดีกว่า


0

นอกจากนี้เล็กน้อยจากทั้งหมด:

ใช้งานได้ดีขึ้นmapเมื่อคุณต้องการรับองค์ประกอบตามช่วงเวลาเนื่องจากมีการจัดเรียงและคุณสามารถทำซ้ำองค์ประกอบเหล่านั้นจากขอบเขตหนึ่งไปอีกองค์ประกอบหนึ่ง


-1

จาก: http://www.cplusplus.com/reference/map/map/

"ภายในองค์ประกอบต่าง ๆ ในแผนที่จะถูกจัดเรียงตามคีย์ของมันเสมอตามเกณฑ์การจัดลำดับอ่อนที่เข้มงวดโดยเฉพาะซึ่งระบุโดยวัตถุเปรียบเทียบภายใน (ของประเภทเปรียบเทียบ)

โดยทั่วไปคอนเทนเนอร์แมปจะช้ากว่าคอนเทนเนอร์ unordered_map เพื่อเข้าถึงองค์ประกอบแต่ละรายการด้วยคีย์ แต่จะอนุญาตให้ทำซ้ำโดยตรงกับชุดย่อยตามคำสั่งของพวกเขา "

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