ฉันกำลังสำรวจHashSet<T>
ประเภท แต่ฉันไม่เข้าใจว่ามันอยู่ที่ใดในคอลเล็กชัน
สามารถใช้แทน a List<T>
? ฉันจินตนาการว่าประสิทธิภาพของ a HashSet<T>
จะดีขึ้น แต่ฉันไม่เห็นการเข้าถึงองค์ประกอบของแต่ละบุคคล
เป็นเพียงการแจงนับเท่านั้น?
ฉันกำลังสำรวจHashSet<T>
ประเภท แต่ฉันไม่เข้าใจว่ามันอยู่ที่ใดในคอลเล็กชัน
สามารถใช้แทน a List<T>
? ฉันจินตนาการว่าประสิทธิภาพของ a HashSet<T>
จะดีขึ้น แต่ฉันไม่เห็นการเข้าถึงองค์ประกอบของแต่ละบุคคล
เป็นเพียงการแจงนับเท่านั้น?
คำตอบ:
สิ่งที่สำคัญเกี่ยวกับการHashSet<T>
มีสิทธิในชื่อ: มันเป็นชุด สิ่งเดียวที่คุณสามารถทำได้ในชุดเดียวคือการกำหนดว่าสมาชิกคืออะไรและตรวจสอบว่ารายการนั้นเป็นสมาชิกหรือไม่
การถามว่าคุณสามารถดึงองค์ประกอบเดียวได้หรือไม่ (เช่นset[45]
) เป็นการเข้าใจแนวคิดของชุดนั้นผิด ไม่มีสิ่งที่เรียกว่าองค์ประกอบที่ 45 ของเซต สินค้าในชุดไม่มีการสั่งซื้อ ชุด {1, 2, 3} และ {2, 3, 1} เหมือนกันทุกประการเนื่องจากมีสมาชิกภาพเหมือนกันและการเป็นสมาชิกก็สำคัญ
มันค่อนข้างอันตรายที่จะทำซ้ำHashSet<T>
เนื่องจากการทำเช่นนั้นจะทำให้เกิดการสั่งซื้อสินค้าในชุด คำสั่งนั้นไม่ได้เป็นสมบัติของชุด คุณไม่ควรพึ่งพามัน หากการสั่งซื้อสินค้าในคอลเลกชันเป็นสิ่งสำคัญสำหรับคุณคอลเลกชันนั้นจะไม่ใช่ชุด
ชุดมีจำนวน จำกัด และมีสมาชิกที่ไม่ซ้ำใคร ในทางกลับกันมันเร็วมาก
HashSet
ไม่ได้กำหนดไว้ดังนั้นอย่าพึ่งพาคำสั่งของตัววนซ้ำ หากคุณทำซ้ำชุดนี้เนื่องจากคุณกำลังทำอะไรบางอย่างกับรายการในชุดสิ่งนั้นจะไม่เป็นอันตรายเว้นแต่คุณจะพึ่งพาสิ่งที่เกี่ยวข้องกับคำสั่งซื้อ A SortedSet
มีคุณสมบัติทั้งหมดของคำสั่งHashSet
บวกอย่างไรก็ตามSortedSet
ไม่ได้มาจากHashSet
; จัดเรียงใหม่SortedSet คือชุดของวัตถุที่แตกต่างกันตามลำดับ
นี่คือตัวอย่างที่แท้จริงของสถานที่ที่ฉันใช้ a HashSet<string>
:
ส่วนหนึ่งของการเน้นไวยากรณ์ของฉันสำหรับไฟล์ UnrealScript เป็นคุณลักษณะใหม่ที่ความคิดเห็นไฮไลท์ Doxygen สไตล์ ฉันจำเป็นต้องสามารถบอกได้ว่า a @
หรือ\
คำสั่งนั้นถูกต้องเพื่อพิจารณาว่าจะแสดงเป็นสีเทา (ถูกต้อง) หรือสีแดง (ไม่ถูกต้อง) ฉันมีHashSet<string>
คำสั่งที่ถูกต้องทั้งหมดดังนั้นเมื่อใดก็ตามที่ฉันกด@xxx
โทเค็นในตัวเล็กซ์ฉันจะใช้validCommands.Contains(tokenText)
เป็นการตรวจสอบความถูกต้อง O (1) ของฉัน ฉันไม่สนใจอะไรเลยนอกจากการมีอยู่ของคำสั่งในชุดคำสั่งที่ถูกต้อง ลองดูทางเลือกอื่นที่ฉันเผชิญ:
Dictionary<string, ?>
: ประเภทใดที่ฉันใช้สำหรับค่า? ContainsKey
ค่าที่มีความหมายตั้งแต่ฉันแค่ไปกับการใช้งาน หมายเหตุ: ก่อนหน้า. NET 3.0 นี่เป็นทางเลือกเดียวสำหรับการค้นหา O (1) - HashSet<T>
ถูกเพิ่มสำหรับ 3.0 และขยายเพื่อใช้งานISet<T>
สำหรับ 4.0List<string>
: ถ้าฉันจัดเรียงรายการไว้ฉันสามารถใช้ได้BinarySearch
ซึ่งก็คือ O (log n) (ไม่เห็นข้อเท็จจริงนี้ที่กล่าวถึงข้างต้น) อย่างไรก็ตามเนื่องจากรายการคำสั่งที่ถูกต้องของฉันเป็นรายการคงที่ที่ไม่เคยเปลี่ยนแปลงสิ่งนี้จะไม่เหมาะสมไปกว่า ...string[]
: อีกครั้งArray.BinarySearch
ให้ประสิทธิภาพ O (log n) หากรายการสั้นนี่อาจเป็นตัวเลือกที่มีประสิทธิภาพดีที่สุด มันก็จะมีค่าใช้จ่ายน้อยกว่าพื้นที่HashSet
, หรือDictionary
List
ถึงแม้BinarySearch
จะไม่เร็วกว่าสำหรับชุดใหญ่ แต่สำหรับชุดเล็กก็ควรค่าแก่การทดลอง ของฉันมีหลายร้อยรายการดังนั้นฉันจึงส่งต่อไปHashSet<T>
ดำเนินการICollection<T>
อินเตอร์เฟซ:
public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
// Methods
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
// Properties
int Count { get; }
bool IsReadOnly { get; }
}
การList<T>
ดำเนินการIList<T>
ซึ่งขยายไฟล์ICollection<T>
public interface IList<T> : ICollection<T>
{
// Methods
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
// Properties
T this[int index] { get; set; }
}
HashSet ได้กำหนดความหมายโดยใช้แฮชแท็กภายใน:
ชุดคือคอลเล็กชันที่ไม่มีองค์ประกอบที่ซ้ำกันและองค์ประกอบที่ไม่เรียงลำดับกัน
HashSet ได้รับอะไรบ้างหากสูญเสียพฤติกรรมดัชนี / ตำแหน่ง / รายการ
การเพิ่มและการดึงรายการจาก HashSet จะเกิดขึ้นโดยตัวออบเจ็กต์เองเสมอไม่ใช่ผ่านตัวสร้างดัชนีและใกล้เคียงกับการดำเนินการ O (1) (รายการคือ O (1) เพิ่ม, O (1) ดึงโดยดัชนี, O (n) ค้นหา / ลบ)
พฤติกรรมของ HashSet สามารถเปรียบเทียบได้กับการใช้ a Dictionary<TKey,TValue>
โดยเพิ่ม / ลบคีย์เป็นค่าเท่านั้นและละเว้นค่าพจนานุกรมเอง คุณคาดว่าคีย์ในพจนานุกรมจะไม่มีค่าซ้ำกันและนั่นคือจุดสำคัญของส่วน "Set"
ประสิทธิภาพจะเป็นเหตุผลที่ไม่ดีในการเลือก HashSet over List สิ่งใดที่จับเจตนาของคุณได้ดีกว่ากัน? หากคำสั่งซื้อมีความสำคัญ Set (หรือ HashSet) จะไม่ทำงาน หากมีการอนุญาตให้ทำซ้ำได้เช่นเดียวกัน แต่มีหลายสถานการณ์ที่เราไม่สนใจคำสั่งซื้อและเราไม่อยากมีรายการที่ซ้ำกัน - และนั่นคือเวลาที่คุณต้องการ Set
Performance would be a bad reason to choose HashSet over List
: ฉันไม่เห็นด้วยกับคุณ นั่นเป็นการบอกว่าการเลือก Dictionray แทนสองรายการไม่ได้ช่วยในการแสดง ดูบทความต่อไปนี้
string[].Contains
และHashSet<string>.Contains
แสดงเจตนาของคุณได้ดีเท่า ๆ กัน เหตุผลในการเลือก HashSet ก็คือมันจะทำงานได้เร็วขึ้นมาก
HashSet เป็นชุดที่ดำเนินการโดยการแฮช ชุดคือชุดของค่าที่ไม่มีองค์ประกอบที่ซ้ำกัน โดยทั่วไปค่าในชุดจะไม่เรียงลำดับ ไม่จึงไม่สามารถใช้ชุดเพื่อแทนที่รายการได้ (เว้นแต่ว่าคุณควรใช้ชุดตั้งแต่แรก)
หากคุณสงสัยว่าชุดใดที่ดีสำหรับ: ทุกที่ที่คุณต้องการกำจัดรายการที่ซ้ำกันอย่างชัดเจน ตามตัวอย่างที่มีการปรับปรุงเล็กน้อยสมมติว่าคุณมีรายการซอฟต์แวร์ที่แก้ไขแล้ว 10.000 รายการและคุณต้องการทราบจำนวนคนที่มีส่วนร่วมในโครงการนั้น คุณสามารถใช้ a Set<string>
และทำซ้ำในรายการการแก้ไขและเพิ่มผู้เขียนของการแก้ไขแต่ละคนในชุด เมื่อคุณทำซ้ำขนาดของชุดคือคำตอบที่คุณกำลังมองหา
HashSet จะถูกใช้เพื่อลบองค์ประกอบที่ซ้ำกันในคอลเลกชันที่ไม่สามารถคำนวณได้ของ IE ตัวอย่างเช่น,
List<string> duplicatedEnumrableStrings = new List<string> {"abc", "ghjr", "abc", "abc", "yre", "obm", "ghir", "qwrt", "abc", "vyeu"};
HashSet<string> uniqueStrings = new HashSet(duplicatedEnumrableStrings);
หลังจากรันโค้ดเหล่านั้นแล้ว uniqueStrings จะถือ {"abc", "ghjr", "yre", "obm", "qwrt", "vyeu"};
อาจใช้บ่อยที่สุดสำหรับแฮชเซ็ตคือการดูว่ามีองค์ประกอบบางอย่างหรือไม่ซึ่งใกล้เคียงกับการดำเนินการ O (1) สำหรับพวกเขา (สมมติว่ามีฟังก์ชันการแฮชที่แข็งแกร่งเพียงพอ) ซึ่งต่างจากรายการที่ตรวจสอบการรวมเป็น O ( n) (และชุดที่เรียงลำดับซึ่งเป็น O (log n)) ดังนั้นหากคุณทำการตรวจสอบเป็นจำนวนมากไม่ว่าจะมีรายการอยู่ในบางรายการหรือไม่ hahssets อาจช่วยเพิ่มประสิทธิภาพได้ หากคุณทำซ้ำเพียงครั้งเดียวจะไม่มีความแตกต่างมากนัก (การทำซ้ำทั้งชุดคือ O (n) เช่นเดียวกับรายการและแฮชเซ็ตจะมีค่าใช้จ่ายค่อนข้างมากกว่าเมื่อเพิ่มรายการ)
และไม่คุณไม่สามารถทำดัชนีชุดซึ่งจะไม่สมเหตุสมผลอยู่ดีเพราะชุดไม่ได้เรียงลำดับ หากคุณเพิ่มบางรายการชุดจะไม่จำว่ารายการใดเป็นรายการแรกและรายการที่สองเป็นต้น
HashSet<T>
เป็นโครงสร้างข้อมูลในกรอบ. NET ที่สามารถแสดงชุดทางคณิตศาสตร์เป็นวัตถุ ในกรณีนี้จะใช้รหัสแฮช ( GetHashCode
ผลลัพธ์ของแต่ละรายการ) เพื่อเปรียบเทียบความเท่าเทียมกันขององค์ประกอบชุด
ชุดแตกต่างจากรายการที่อนุญาตให้เกิดองค์ประกอบเดียวกันที่มีอยู่ภายในได้เพียงรายการเดียว HashSet<T>
จะกลับมาfalse
ถ้าคุณพยายามเพิ่มองค์ประกอบที่สองที่เหมือนกัน อันที่จริงการค้นหาองค์ประกอบนั้นรวดเร็วมาก ( O(1)
เวลา) เนื่องจากโครงสร้างข้อมูลภายในเป็นเพียงแฮชแท็ก
หากคุณสงสัยว่าควรใช้ตัวList<T>
ไหนโปรดทราบว่าการใช้ตำแหน่งที่HashSet<T>
เหมาะสมไม่ใช่ข้อผิดพลาดที่ใหญ่ที่สุดแม้ว่าอาจทำให้เกิดปัญหาที่คุณมีรายการซ้ำที่ไม่พึงปรารถนาในคอลเลกชันของคุณ ยิ่งไปกว่านั้นการค้นหา (การเรียกค้นรายการ) นั้นมีประสิทธิภาพมากกว่าอย่างมาก - O(1)
ตามหลักการแล้ว (เพื่อการจัดเก็บข้อมูลที่สมบูรณ์แบบ) แทนที่จะเป็นO(n)
เวลาซึ่งค่อนข้างสำคัญในหลาย ๆ สถานการณ์
List<T>
ใช้เพื่อจัดเก็บชุดข้อมูลที่สั่งซื้อ หากคุณทราบลำดับสัมพัทธ์ขององค์ประกอบของรายการคุณสามารถเข้าถึงได้ในเวลาคงที่ อย่างไรก็ตามในการพิจารณาว่าองค์ประกอบอยู่ที่ใดในรายการหรือตรวจสอบว่ามีอยู่ในรายการหรือไม่เวลาในการค้นหาจะเป็นแบบเส้นตรง ในทางกลับกันHashedSet<T>
ไม่รับประกันลำดับของข้อมูลที่จัดเก็บและส่งผลให้เวลาในการเข้าถึงองค์ประกอบคงที่
เป็นชื่อที่แสดงถึงHashedSet<T>
เป็นโครงสร้างข้อมูลที่ดำเนินการกำหนดความหมาย โครงสร้างข้อมูลได้รับการปรับให้เหมาะสมเพื่อใช้การดำเนินการชุด (เช่น Union, Difference, Intersect) ซึ่งไม่สามารถทำได้อย่างมีประสิทธิภาพกับการใช้งาน List แบบเดิม
ดังนั้นในการเลือกประเภทข้อมูลที่จะใช้ขึ้นอยู่กับว่าคุณกำลังพยายามทำอะไรกับแอปพลิเคชันของคุณ หากคุณไม่สนใจเกี่ยวกับวิธีการที่องค์ประกอบของคุณจะได้รับคำสั่งในการเก็บรวบรวมและมีเพียงต้องการที่จะ enumarate หรือตรวจสอบสำหรับการดำรงอยู่, HashSet<T>
การใช้งาน มิฉะนั้นให้พิจารณาใช้List<T>
หรือโครงสร้างข้อมูลอื่นที่เหมาะสม
ในสถานการณ์จำลองพื้นฐานHashSet<T>
ควรใช้เมื่อคุณต้องการการดำเนินการชุดที่เฉพาะเจาะจงมากขึ้นในสองคอลเลกชันมากกว่าที่ LINQ ให้ไว้ วิธีการ LINQ ชอบDistinct
, Union
, Intersect
และExcept
มีความเพียงพอในสถานการณ์ส่วนใหญ่ แต่บางครั้งคุณอาจจะต้องดำเนินงานเม็ดเล็กมากขึ้นและHashSet<T>
ให้:
UnionWith
IntersectWith
ExceptWith
SymmetricExceptWith
Overlaps
IsSubsetOf
IsProperSubsetOf
IsSupersetOf
IsProperSubsetOf
SetEquals
ความแตกต่างอีกอย่างระหว่างวิธีการ LINQ และHashSet<T>
"การทับซ้อนกัน" คือ LINQ จะส่งคืนใหม่เสมอIEnumerable<T>
และHashSet<T>
วิธีการแก้ไขการรวบรวมแหล่งที่มา
ในระยะสั้น - เมื่อใดก็ตามที่คุณถูกล่อลวงให้ใช้พจนานุกรม (หรือพจนานุกรมโดยที่ S เป็นสมบัติของ T) คุณควรพิจารณา HashSet (หรือ HashSet + ที่ใช้ IEquatable บน T ซึ่งเท่ากับ S)
SortedSet
โครงสร้างข้อมูลอาจขัดแย้งกับสิ่งที่คุณพูดเกี่ยวกับคำสั่งที่ไม่เป็นสมบัติของชุดหรือชี้ให้เห็นถึงความเข้าใจผิดจากทีมพัฒนา