สิ่งที่. NET คอลเลกชันให้การค้นหาที่เร็วที่สุด


143

ฉันมีรายการ 60k ที่ต้องตรวจสอบกับรายการค้นหา 20k มีวัตถุคอลเลกชัน (เช่นList, HashTable) ที่ให้Contains()วิธีการที่รวดเร็วเป็นพิเศษหรือไม่? หรือฉันจะต้องเขียนของตัวเอง? ใน otherwords เป็นContains()วิธีการเริ่มต้นเพียงสแกนแต่ละรายการหรือใช้อัลกอริทึมการค้นหาที่ดีกว่า

foreach (Record item in LargeCollection)
{
    if (LookupCollection.Contains(item.Key))
    {
       // Do something
    }
}

หมายเหตุ รายการค้นหาถูกเรียงลำดับแล้ว


มีสำหรับรายการไม่ทำงานสำหรับรายการวัตถุเนื่องจากเป็นการเปรียบเทียบการอ้างอิง
Fiur

2
จัดเรียงข้อมูลหรือไม่ การค้นหาแบบไบนารี - ดูคำตอบของ @ Mark
Hamish Smith

HashtTable เต้นอะไรถึงรายการ 2m ในประสบการณ์ของฉัน
คริส S

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

2
อย่าลืมเกี่ยวกับ System.Collections.Generic.SortedList (TKey, TValue) หากคุณต้องการทำให้สิ่งนี้ง่ายขึ้น แต่หลีกเลี่ยงการแฮช
Brian

คำตอบ:


141

ในกรณีทั่วไปมากที่สุดพิจารณาSystem.Collections.Generic.HashSetเป็นค่าเริ่มต้นของคุณ "มี" Containsโครงสร้างข้อมูลเทียมเพราะมันต้องใช้เวลาอย่างต่อเนื่องในการประเมิน

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


36
หมายเหตุ: อย่าลืมแทนที่ฟังก์ชัน hashcode เพื่อประสิทธิภาพที่เพิ่มขึ้นให้สร้างรหัสแฮชโค้ดของคุณในตัวสร้าง
Brian

1
@Brian: จุดดี ฉันกำลังสมมติ (ไม่มีมูล) Record.Key เป็นประเภทในตัวบางชนิด
Jimmy

3
@Brian: แทน pregenerating ฉันชอบที่จะเก็บไว้ที่สร้างขึ้นในครั้งแรกทำไมตัวสร้างช้าลงกับสิ่งที่คุณไม่ทราบว่ามันจะถูกนำมาใช้?
jmservera

8
FYI: การทดสอบประสิทธิภาพ - ฉันสร้างการเปรียบเทียบระหว่างรายการ <T> และ HashSet <T> สำหรับสตริง ฉันพบว่า HashSet เร็วกว่ารายการประมาณ 1,000 เท่า
Quango

10
@Quango: 3 ปีต่อมา แต่จริงๆแล้วถ้าคุณไม่ได้ระบุขนาดของข้อมูลของคุณตั้งค่าการเปรียบเทียบประสิทธิภาพนี้หมายความว่าไม่มีอะไร: Hashsets มีการค้นหา O (1) รายการมีการค้นหา O (n) ดังนั้นอัตราส่วนประสิทธิภาพเป็นสัดส่วน n
Clément

73

หากคุณไม่ต้องการสั่งซื้อลองHashSet<Record>(ใหม่ถึง. Net 3.5)

ถ้าคุณทำใช้และโทรList<Record>BinarySearch


8
หรือใน. NET> = 4 ให้ใช้SortedSet
StriplingWarrior

2
หรือดีกว่าImmutableSortedSetจาก System.ImmutableCollections
Alexei S

24

คุณเคยคิดList.BinarySearch(item)ไหม

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


1
คุณพูดถูกแฮชอาจทำให้เกิดปัญหาที่ไม่พึงประสงค์เมื่อใช้วัตถุที่ไม่แน่นอนเป็นกุญแจ
jmservera

10

คุณควรอ่านบล็อกนี้ที่ทดสอบความเร็วของคอลเลกชันและวิธีการที่หลากหลายสำหรับแต่ละประเภทโดยใช้เทคนิคทั้งแบบเดี่ยวและแบบมัลติเธรด

จากผลการค้นหา BinarySearch ในรายการและ SortedList เป็นนักแสดงชั้นนำที่ทำงานอย่างต่อเนื่องที่คอเมื่อค้นหาบางอย่างเป็น "ค่า"

เมื่อใช้คอลเลกชันที่อนุญาตให้ "คีย์", พจนานุกรม, ConcurrentDictionary, Hashset และ HashTables ดำเนินการโดยรวมที่ดีที่สุด


4

เก็บรายการทั้งสองไว้ x และ y เรียงตามลำดับ

ถ้า x = y ให้ดำเนินการของคุณถ้า x <y, ล่วงหน้า x, ถ้า y <x, ให้เลื่อน y จนกว่ารายการใดรายการหนึ่งจะว่างเปล่า

เวลาทำงานของสี่แยกนี้เป็นสัดส่วนกับ min (size (x), size (y))

อย่าเรียกใช้วน. contains () นี่เป็นสัดส่วนกับ x * y ซึ่งแย่กว่ามาก


+1 สำหรับอัลกอริทึมที่มีประสิทธิภาพมากขึ้น แม้ว่ารายการจะไม่ได้เรียงลำดับในขณะนี้ก็จะมีประสิทธิภาพมากขึ้นในการจัดเรียงพวกเขาก่อนแล้วเรียกใช้อัลกอริทึมนี้
Matt Boehm

รันไทม์จะไม่เป็นสัดส่วนกับ max (size (x), size (y)) ในสถานการณ์กรณีที่เลวร้ายที่สุดใช่ไหม? ตัวอย่าง: int [] x = {99,100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}} int;
Matt Boehm

ไม่เพราะเมื่อคุณเสร็จสิ้นการตั้งค่าที่เล็กกว่าคุณสามารถผนวกองค์ประกอบที่เหลือจากชุดที่มีขนาดใหญ่กว่าเพราะพวกเขาจะถูกจัดเรียงแล้ว ฉันคิดว่ากระบวนการนี้คล้ายกับ Merge Sort

3

หากเป็นไปได้ที่จะเรียงลำดับรายการของคุณมีวิธีที่เร็วกว่ามากในการทำเช่นนี้จากนั้นทำการค้นหาคีย์ลงใน hashtable หรือ b-tree แม้ว่าคุณจะเป็นไอเท็มที่ไม่สามารถจัดเรียงได้ แต่คุณไม่สามารถใส่มันลงไปในทรี b ได้เลย

อย่างไรก็ตามถ้าเรียงลำดับรายการทั้งสองได้แล้วมันเป็นเพียงเรื่องของการเดินรายการค้นหาตามลำดับ

Walk lookup list
   While items in check list <= lookup list item
     if check list item = lookup list item do something
   Move to next lookup list item

ใช่จริง หากคุณมีรายการที่เรียงสองรายการคุณจะต้องสำรวจแต่ละครั้ง
เดนเวอร์

3

หากคุณใช้. Net 3.5 คุณสามารถสร้างโค้ดที่สะอาดกว่าโดยใช้:

foreach (Record item in LookupCollection.Intersect(LargeCollection))
{
  //dostuff
}

ฉันไม่มี. Net 3.5 ที่นี่และดังนั้นจึงไม่ได้ทดสอบ มันขึ้นอยู่กับวิธีการขยาย ไม่ว่าLookupCollection.Intersect(LargeCollection)อาจจะไม่เหมือนLargeCollection.Intersect(LookupCollection)... หลังอาจช้ากว่ามาก

นี่ถือว่า LookupCollection เป็น HashSet


2

หากคุณไม่กังวลเกี่ยวกับการส่งมอบประสิทธิภาพการทำงานทุกบิตสุดท้ายข้อเสนอแนะในการใช้ HashSet หรือการค้นหาแบบไบนารี่เป็นสิ่งที่มั่นคง ชุดข้อมูลของคุณไม่ใหญ่พอที่จะเป็นปัญหา 99% ของเวลา

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

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