HashSet <T> เทียบกับ Dictionary <K, V> wrt เวลาค้นหาเพื่อดูว่ามีรายการอยู่หรือไม่


103
HashSet<T> t = new HashSet<T>();
// add 10 million items


Dictionary<K, V> t = new Dictionary<K, V>();
// add 10 million items.

.Containsวิธีของใครจะกลับเร็วกว่ากัน?

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


1
ขั้นตอนที่ 1:ดูว่าทั้งสองทำสิ่งเดียวกันหรือไม่ (ในกรณีนี้ทั้งสองคอลเลคชันมีวัตถุประสงค์ที่แตกต่างกัน) ขั้นตอนที่ 2:อ้างอิงเอกสารและดูว่าคุณรู้สึกดีกับความซับซ้อนแบบไม่แสดงอาการหรือไม่ ขั้นตอนที่ 3:หากคุณรู้สึกว่าคุณต้องกังวลมากขึ้นให้วัดตัวเองแล้วถามคำถามโดยโพสต์เกณฑ์มาตรฐานควบคู่ไปด้วย ในกรณีของคุณคำถามจะไม่มีจุดหมายในขั้นตอนแรก
nawfal

คำตอบ:


153

HashSet VS รายการเทียบกับการทดสอบประสิทธิภาพพจนานุกรมนำมาจากที่นี่

เพิ่มวัตถุ 1000000 รายการ (โดยไม่ต้องตรวจสอบรายการที่ซ้ำกัน)

มีการตรวจสอบวัตถุครึ่งหนึ่งของคอลเลกชัน 10,000

ลบวัตถุครึ่งหนึ่งของคอลเลกชัน 10,000 ชิ้น


9
วิเคราะห์เยี่ยม! ดูเหมือนว่า .Contains for Dictionary นั้นเร็วมากจนไม่มีประโยชน์จากการใช้ HashSet เลยในกรณีของ OP
EtherDragon

2
ใช่ฉันมีคำถามเดียวกันกับ OP ฉันมีพจนานุกรมอยู่แล้วฉันใช้ด้วยเหตุผลอื่นและต้องการทราบว่าฉันได้รับประโยชน์จากการเปลี่ยนเป็นแฮชเซ็ตแทนการใช้ประกอบด้วยคีย์หรือไม่ ดูเหมือนว่าคำตอบคือไม่เนื่องจากทั้งคู่เร็วมาก
FistOfFury

4
ตรงกันข้ามกับสิ่งที่ความคิดเห็นก่อนหน้านี้ดูเหมือนจะบอกเป็นนัยว่าใช่คุณควรเปลี่ยนไปใช้ HashSet เพราะให้สิ่งที่คุณต้องการ: การจัดเก็บชุดของค่า (ซึ่งต่างจากการรักษาการทำแผนที่บางประเภทไว้) คำตอบนี้บ่งชี้ว่าจะไม่มีผลกระทบด้านลบต่อประสิทธิภาพเมื่อเทียบกับ Dictionary
Francois Beaussier

คำตอบนี้ไม่ได้บอกคุณว่าประสิทธิภาพของ HashSet และ Dictionary เปรียบเทียบกันอย่างไร ... ทั้งหมดนี้บอกคุณได้ว่าทั้งคู่เร็วกว่า List .. อืม ... ใช่แล้ว! ชัด! HashSet อาจเร็วขึ้น 3 เท่าและคุณจะไม่รู้เพราะการทดสอบที่เกี่ยวข้องได้ยุบทั้งสองรายการเป็น "ทันที ... เมื่อเทียบกับรายการ "
Brondahl

71

ฉันคิดว่าคุณหมายถึงDictionary<TKey, TValue>ในกรณีที่สอง? HashTableเป็นคลาสที่ไม่ใช่คลาสทั่วไป

คุณควรเลือกคอลเลกชันที่เหมาะสมสำหรับงานตามความต้องการจริงของคุณ คุณต้องการแมปแต่ละคีย์กับค่าหรือไม่? ถ้าใช่ให้ใช้Dictionary<,>. หากคุณเพียงHashSet<>ดูแลเกี่ยวกับว่ามันเป็นชุดที่ใช้

ฉันคาดหวังHashSet<T>.ContainsและDictionary<TKey, TValue>.ContainsKey(ซึ่งเป็นการดำเนินการที่เทียบเคียงได้โดยสมมติว่าคุณกำลังใช้พจนานุกรมของคุณอย่างสมเหตุสมผล) ในการดำเนินการโดยทั่วไป - พวกเขาใช้อัลกอริทึมเดียวกันโดยพื้นฐานแล้ว ฉันเดาว่ารายการที่Dictionary<,>มีขนาดใหญ่ขึ้นคุณจะมีโอกาสที่จะเป่าแคชได้Dictionary<,>มากกว่าHashSet<>แต่ฉันคาดว่าจะไม่มีนัยสำคัญเมื่อเทียบกับความเจ็บปวดจากการเลือกประเภทข้อมูลที่ไม่ถูกต้องในแง่ของสิ่งที่คุณเป็น พยายามที่จะบรรลุ


ใช่ฉันหมายถึง Dictionary <TKey, TValue> ฉันกังวลเกี่ยวกับการค้นหาสำหรับการดำรงอยู่ของรายการในโครงสร้างข้อมูลที่เป็นทั้งหมด
halivingston

3
@halivingston ในกรณีนั้นให้ใช้ HashSet ทำให้เห็นได้ชัดว่านั่นคือทั้งหมดที่คุณต้องการ
Jon Skeet

2
โอเคขอบคุณ. ตอนนี้ฉันมี HashSet <TKey> และสำเนา Dictionary <Tkey, TValue> ที่ซ้ำกันอยู่ในหน่วยความจำ ก่อนอื่นประกอบด้วย HashSet จากนั้นเรียกคืนค่าใน Dictionary <TKey, TValue> ตอนนี้ฉันมีความจำไม่สิ้นสุด แต่ในไม่ช้าฉันกลัวว่าความจำของฉันจะถูก จำกัด และทีมของเราจะขอให้ฉันลบสิ่งที่ซ้ำกันนี้ออกจากหน่วยความจำซึ่งในตอนนี้ฉันจะถูกบังคับให้ใช้ Dictionary <TKey, TValue>
halivingston

4
คุณรู้หรือไม่ว่า Dictionary มีฟังก์ชันประกอบด้วยคีย์ด้วยใช่ไหม? เหตุใดคุณจึงทำซ้ำข้อมูล
Blindy

8
หากคุณมีข้อมูลในพจนานุกรมอยู่แล้วแสดงว่าความคิดเห็นแรกของคุณไม่ถูกต้องอย่างชัดเจน - คุณต้องเชื่อมโยงคีย์กับค่าด้วย อาจจะไม่ใช่สำหรับโค้ดนี้โดยเฉพาะ แต่ไม่เกี่ยวข้อง หากคุณมีDictionaryเหตุผลอื่นอยู่แล้วคุณควรใช้สิ่งนั้น
Jon Skeet

7

จากเอกสาร MSDN สำหรับ Dictionary <TKey, TValue>

"การดึงค่าโดยใช้คีย์นั้นเร็วมากใกล้เคียงกับO (1)เนื่องจากคลาส Dictionary ถูกใช้เป็นตารางแฮช "

ด้วยหมายเหตุ:

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

ฉันรู้ว่าคำถาม / โพสต์ของคุณเก่า - แต่ในขณะที่กำลังหาคำตอบสำหรับคำถามที่คล้ายกันฉันก็เจอสิ่งนี้

หวังว่านี่จะช่วยได้ เลื่อนลงไปที่ส่วนหมายเหตุเพื่อดูรายละเอียดเพิ่มเติม https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

นี่คือโครงสร้างข้อมูลที่แตกต่างกัน นอกจากนี้ยังไม่มีเวอร์ชันทั่วไปของHashTable.

HashSetมีค่าประเภท T ซึ่งHashTable(หรือDictionary) มีคู่คีย์ - ค่า ดังนั้นคุณควรเลือกการรวบรวมข้อมูลที่คุณต้องการจัดเก็บ


0

คำตอบที่ยอมรับสำหรับคำถามนี้ไม่สามารถตอบคำถามได้อย่างถูกต้อง! เกิดขึ้นเพื่อให้คำตอบที่ถูกต้อง แต่คำตอบนั้นไม่ได้แสดงโดยหลักฐานที่พวกเขาให้มา

คำตอบนั้นแสดงให้เห็นว่าการค้นหาคีย์บน a DictionaryหรือHashSetเร็วกว่าการค้นหาในไฟล์List. ซึ่งเป็นเรื่องจริง แต่ไม่น่าสนใจและไม่น่าแปลกใจหรือพิสูจน์ได้ว่ามีความเร็วเท่ากัน

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

โดยเฉพาะอย่างยิ่งการค้นหา 100,000,000 ครั้งใช้เวลาระหว่าง 10 ถึง 11.5 วินาทีสำหรับทั้งสองอย่างสำหรับฉันในการทดสอบนี้

รหัสทดสอบ:

private const int TestReps = 100_000_000;
[Test]
public void CompareHashSetContainsVersusDictionaryContainsKey()
{
    for (int j = 0; j < 10; j++)
    {
        var rand = new Random();
        var dict = new Dictionary<int, int>();
        var hash = new HashSet<int>();

        for (int i = 0; i < TestReps; i++)
        {
            var key = rand.Next();
            var value = rand.Next();
            hash.Add(key);
            dict.TryAdd(key, value);
        }

        var testPoints = Enumerable.Repeat(1, TestReps).Select(_ => rand.Next()).ToArray();
        var timer = new Stopwatch();
        var total = 0;
        
        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (hash.Contains(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);
        
        var target = total;
        Assert.That(total == target);
        

        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (dict.ContainsKey(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);

        Assert.That(total == target * 2);
        Console.WriteLine("Set");
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.