الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Collecting Data Items -Hashtable, SortedList, ListDictionary, HybridDictionary, OrderedDictionary-

مغلق
بدأه عبد الكريم كنعان في 7 أغسطس 2007 · 4 رد · 1,100 مشاهدة · في Microsoft Visual C#.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

التعامل مع القواميس Dictionaries:

بالإضافة إلى ما تعلمناه سابقا فإنه يوجد ضمن فضاء الأسماء System.Collections صفوف القواميس التي تدل من اسمها أنها مثل القاموس تقوم بتخزين قيمة تسمى "مفتاح" وقيمة تسمى "قيمة المفتاح"، وبالتالي تكون الافادة في عملية البحث عن القيمة المقابلة لمفتاح ما، من هذه الصفوف الصف Hashtable الذي يمكن استخدامه كما يلي:

Hashtable emailLookup = new Hashtable();
emailLookup.Add("Abdulkarim2006@hotmail.com", "Kanaan, Abdulkarim");
emailLookup["Abdulkarim.Kanaan@gmail.com"] = "Kanaan, Abdulkarim";

طبعا كما لاحظنا هناك طريقتين لإدخال البيانات إما باستخدام التابع Add أو باستخدام الفهرسة طبعا ولكن يجدر القول إلى أن هذه الأخيرة ستكون عملية إضافة في حال أن المفتاح الذي تمت إضافته غير موجود أما في حال وجوده مسبقا فإنه سيؤدي إلى عملية تعديل، أي في حال الايميل Abdulkarim.Kanaan@gmail.com موجود مسبقا فإن السطر:

emailLookup["Abdulkarim.Kanaan@gmail.com"] = "Kanaan, Abdulkarim";

يعد عملية تعديل وليس إضافة.، وبالتالي نجد أن هذا الصف المعلومات المخزنة فيه عبارة عن زوج (مفتاح\قيمة)، والآن من أجل الحصول على قيمة مقابلة لمفتاح يكفي أن نكتب:

Console.WriteLine(emailLookup["Abdulkarim.Kanaan@gmail.com"]);

ولكن في حال أننا لا نعرف المفتاح للقيمة التي نبحث عنها على المعلومات المخزنة داخل الكائن emailLookup من الصف Hashtable دعنا نكتب:

foreach (object obj in emailLookup)
{
	Console.WriteLine(obj);
}

ولكن ياللمفاجأة ما هذه النواتج؟:

System.Collections.DictionaryEntry

System.Collections.DictionaryEntry

ما الذي حدث؟ الذي حدث أنك حصلت على كائن من الصف DictionaryEntry وليس مفتاح أو قيمة للمفتاح، حتى يتم العمل بشكل سليم عليك أن تنشئ كائن من الصف DictioaryEntry كما في المثال التالي:

foreach (DictionaryEntry entry in emailLookup)
{
	Console.WriteLine(entry.Value);
}

جميع صفوف القواميس تدعم الواجهة IDictionary المشتقة من الواجهة ICollection، أهم الخصائص والاجراءات للواجهة IDictionary مبينة في الجدوليت التاليين:

اسم الخاصية الوصف

IsFixedSize هذه القيمة تشير فيما إذا كان بالامكان تغير سعة المجمع.

IsReadOnly تشير هذه القيمة إلى أنه إذا كان بالإمكان تغير قيم المجمع.

Keys يعيد كائن من الواجهة ICollection يحتوي على قائمة المفاتيح المخزنة داخل المجمع.

Values يعيد كائن من الواجهة ICollection يحتوي على قائمة قيم المفاتيح المخزنة داخل المجمع.

اسم الاجراء الوصف

Add إضافة زوج (مفتاح\قيمة) إلى المجمع.

Clear حذف كافة عناصر المجمع.

Contains التحقق فبما إذا كان مفتاح محدد موجود داخل المجمع.

GetEnumerator يعيعد كائن من الواجهة IDictionaryEnumerator، هذا الاجراء مختلف عن ذلك الذي يعيد كائن من الواجهة IEnumerable

Remove يحذف عنصر من المجمع وذلك من خلال مفتاح محدد.

أو يمكن الحصول على القيم المخزنة داخل مجمع كما يلي:

foreach (object obj in emailLookup.Values)
{
	Console.WriteLine(obj);
}

بالإضافة إلى ذلك فإن الصف Hashtable يحوي عل تابعين من أجل معرفة فيما إذا كان فتاح أو قيمة مفتاح موجودة داخل المجمع، الجول التالي يوضحهما:

اسم الاجراء الوصف

ContainsKey يتحقق فيما إذا كان المجمع يحتوي على مفتاح محدد.

ContainsValue يتحقق فيما إذا كان المجمع يحتوي على قيمة مفتاح محدد.

فهم عملية المساواة (التساوي) Equality:

يستخدم الصف Hashtable قيمة من نمط العدد الصحيح تقابل المفتاح، يستخدم هذه القيمة من أجل تسريع عمليات البحث عن مفتاح محدد، حيث أن كل صف في .NET مشتق من الصف Object الذي يدعم الاجراء GetHash الذي يعيد بدوره قيمة عددية صحيحة فريدة تعرف الكائن، في الصف Hashtable يتم تخزين قيمة فريدة للمفاتيح المخزنة في نفس الكائن، فعندما نقوم بمحاولة تخزين مفتاح مرتين (هذه الكلمة من ذهب: كيف يعرف أن هذا المفتاح مكرر؟؟ تابع لتعرف السبب) فإن المرة الثانية سوف تعتبر استدعاء لمفتاح الأول من أجل تعديل القيمة المقابلة له، على سبيل المثال:

Hashtable duplicates = new Hashtable();

duplicates["First"] = "1st";
duplicates["First"] = "the first";

Console.WriteLine(duplicates.Count); \\1

بما أن كلا المفتاحين متساويين في القيمة فإنه يعتبر تعديل للقيمة المقابلة للمفتاح الأول، في الحقيقة قد تم ذلك باستدعاء الاجراء GetHashCode الموجود في الصف String وبالرغم من أن كلا منهما (المفتاحين) كائن مستقل إلا أنه تم التعرف عليها على أنهما كائن واحد.

حتى نفهم الأمر الأكثر دعنا ننشىء صف جديد ونسميه Fish كما يلي:

public class Fish
{
	string name;

	public Fish(string theName)
	{
		name = theName;
	}
}

والان إذا قمنا بإنشاء كائنين من هذا الصف الجديد وجعلنا الخاصية name تأخذ نفس القيمة، دعنا نرى ماذا سوف يعتبرهما الكائن من الصف Hashtabel هل هما متساويين أم لا؟

Hashtable duplicates = new Hashtable();

Fish key1 = new Fish("Herring");
Fish key2 = new Fish("Herring");

duplicates[key1] = "Hello";
duplicates[key2] = "Hello";

Console.WriteLine(duplicates.Count); // 2

لقد اعتبر الكائن duplicates كلا من الكائنين key1, key2 كائنين غير متساويين لأن التابع GetHashCode قد أنشىء قيمتين مختلفتين لكل منهما، حتى نعالج هذا الأمر يجب أن نتجاوز المنهج GetHashCode من الصف Object ونعرفه نحن بأنفسنا كما يلي:

public override int GetHashCode()
{
	return name.GetHashCode();
}

والان عندما يتم استدعاء المنهح GetHashCode فإنه سيعيد نفس القيمة وذلك في حال قيمة name متساوية، ولكن مع ذلك لن نكون قد حللنا المشكلة والسبب في ذلك أنه يوجد تابع آخر في الصف Object يدعى Equals فهو يعيد القيمة false إذا كان كلا الكائنين من نسختين مختلفتين (أي لا يحملان نفس العنوان اللذان يشيران له في الذاكرة) ولحل هذه المشكلة نقوم بتجاوز المنهج Equals كما يلي:

public override bool Equals(object obj)
{
	Fish otherFish = obj as Fish;

	if (otherFish == null)
		return false;

	return otherFish.name == this.name;
}

الان أعد تشغيل البرنامج ولا حظ الفرق.

استخدام الواجهة IEqualityComparer: تحتوي هذه الواجهة على تابعين اثنين الأول: GetHashCode والثاني Equals سوف نقوم بالاستفادة من هذه الواجهة فيما يلي لنعد إلى المثال:

Hashtable duplicates = new Hashtable();

duplicates["First"] = "1st";
duplicates["First"] = "the first";

Console.WriteLine(duplicates.Count); \\1

ولنقم بتغير قيمة أحد المفاتيح من First إلى first وأعد التشغيل ستلاحظ أن الكائن من الصف Hashtable قد إعتبرهما كائنين مختلفين، والمطلوب الآن إلغاء التحسس لحالة الأحرف، كيف سنقوم بعمل ذلك؟؟ لاحظ إن الباني للصف Hashtable يقبل كائن من نوع IEqualityComparer سوف تستفيد منها بإنشاء صف يرث من هذه الواجهة ثم تجهيز عملية المقارنة كما نريد:

public class InsensitiveComparer : IEqualityComparer
{
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

	#region IEqualityComparer Members

	public int GetHashCode(object obj)
	{
		return obj.ToString().ToLowerInvariant().GetHashCode();
	}

	public new bool Equals(object x, object y)
	{
		if (_comparer.Compare(x, y) == 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	#endregion
}

أعد كتابة الكود السابق كما يلي:

Hashtable duplicates = new Hashtable(new InsensitiveComparer());

duplicates["First"] = "1st";
duplicates["first"] = "the first";

Console.WriteLine(duplicates.Count); // 1

ولاحظ الناتج!!!.

بقي ذكر شيء واحد أن الصف Hashtable يقوم بترتيب العناصر المدخلة تلقائيا تبعا لقيمة Hash للمفاتيح المدخلة، المثال التالي يوضح ذلك:

Hashtable ht = new Hashtable();
ht.Add(0, "Zero");
ht.Add(2, "Two");
ht.Add(1, "One");

foreach(DictionaryEntry de in ht)
{
	Console.WriteLine("Key:{0}, Value:{1}", de.Key, de.Value); 
}

الناتج سيكون:

Key:2, Value:Two

Key:1, Value:One

Key:0, Value:Zero

Ch4L3Hashtable.zip

تم تعديل هذه المشاركة بواسطة عبد الكريم كنعان في 7 أغسطس 2007 في 05:15

#2

استخدام الصف SortedList:

يعتبر هذا الصف من الصفوف سهلة الاستخدام المثال التالي يوضح كيفية استخدامه:

SortedList sort = new SortedList(new DescendComparer());
sort["First"] = "1st";
sort["Second"] = "2nd";
sort["Third"] = "3rd";
sort["Fourth"] = "4th";
sort["fourth"] = "4th";

foreach (DictionaryEntry entry in sort)
{
	Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
}

خصائص هذا الصف:

اسم الخاصية الوصف

Capacity وهي تمثل عدد العناصر المحجوزة داخل المجموعة سواء أأدخلت قيم فيها أم لم تدخل اي قيمة، وليس هو عدد العناصر المخزنة .

إجراءات الصف SortedList:

اسم الاجراء الوصف

ContainsKey يتحقق فيما إذا كان المجمع يحتوي على مفتاح محدد.

ContainsValue يتحقق فيما إذا كان المجمع يحتوي على قيمة مفتاح محدد.

GetByIndex يعيد القيمة المقابلة لدليل محدد.

GetKey يعيد المفتاح المحدد من خلال دليل محدد.

GetKeyList يعيد قائمة المفاتيح المخزنة مرتبة.

GetValueList يعيد قائمة القيم المخزنة.

IndexOfKey يعيد دليل مفتاح مخزن في المحموعة.

OndexOfvalue يعيد دليل قيمة مخزنة في المجموعة.

RemoveAt يحذف قيمة محددة وذلك من خلال رقم الدليل.

SetByIndex استبدال القيمة وذلك من خلال دليل محدد.

TrimToSize يحذف الأماكن المحجوزة الغير مستخدمة.

يختلف الصف SortedList عن الصف Hashtable أن الكائن من الصف SortedList يقوم بعماية ترتيب العناصر أثناء عملية الادخال بينما الكائن من الصف Hashtable يقوم بعملية الترتيب عند استدعاء المنهج Sort.

Ch4L3SortedList.zip

#3

الصف ListDictionary:

إن الصف Hashtable الذي سبق ذكره مجمع للبيانات مفيد بشكل عام، ولكنه يعاب عليه بأمر واحد أنه يؤثر في الأداء حتى من أجل بيانات صغيرة ، لذلك تم إيجاد الصف ListDictionary حيث يستخدم من أجل تخزين بيانات قليلة به فهو مفيد جدا، من خلال المثال التالي يمكنك التعرف على سهولة استخدام هذا الصف كما يمكنك تطبيق نفس الطرق التي تعلمناها في الصف Hashtable:

ListDictionary emailLookup = new ListDictionary();

emailLookup["Abdulkarim.Kanaan@gmail.com"] = "Kanaan, Abdulkarim";
emailLookup["Abdulkarim2006@hotmail.com"] = "kanaan, Abdulkarim";

foreach (DictionaryEntry d in emailLookup)
{
	Console.WriteLine("{0} = {1}", d.Key, d.Value);
}

Ch4L3ListDictionary.zip

تم تعديل هذه المشاركة بواسطة عبد الكريم كنعان في 7 أغسطس 2007 في 05:14

#4

الصف HybridDictionary:

كما ناقشنا سابقا فإن الصف ListDictionary مفيد إذا كان لدينا كمية بيانات قليلة، هذا يعني إن كنت تعلم أن مجموعة بياناتك قليلة استخدم الصف ListDictionary، وإن كنت تعلم أنها كبيرة استخدم الصف Hashtable، وإن لا كنت تعلم فاستخدم الصف HybridDictionary، إن هذا الصف سهل الاستخدام كسابقيه وعندما تصبح كمية البيانات كبير جدا فإنه يتم تحويله داخليا إلى الصف Hashtable، المثال التالي يوضح سهولة استخدامه:

HybridDictionary emailLookup = new HybridDictionary();

emailLookup["Abdulkarim.Kanaan@gmail.com"] = "Kanaan, Abdulkarim";
emailLookup["Abdulkarim2006@hotmail.com"] = "kanaan, Abdulkarim";

foreach (DictionaryEntry d in emailLookup)
{
	Console.WriteLine("Email:{0}, the owner is:{1}", d.Key, d.Value);
}

Ch4L3ListDictionary.zip

#5

الصف OrderedDictionary:

لقد تعرفنا سابقا على الصف Hashtable وقد عدنا هنا لنبين أمرا يساعد على فهم عمل الصف OrderedDictionary، عندما تقوم بإضافة عناصر إلى كائن من الصف Hashtable، فثمة هناك أمرين:

أولهما أن ليس بإمكانك من خلال دليل العنصر أن تصل إليه.

ثانيهما أن Hashtable يقوم بترتيب العناصر تلقائيا تبعا لقيمة ما تحدثنا عنها سابقا GetHashCode، أي أنه لا يحافظ على ترتيب العناصر المدخلة.

لذلك إذا أردت قاموس سريع يحافظ على ترتيب دخول العناصر فإن .Net زودتنا بالصف OrderedDictionary حيث أن هذا الصف شبيه بالصف Hashtable بالإضافة إلى مجموعة خصائص وإجراءات، الجدول التالي يوضح الخصائص المضافة:

Item تدعم هذه الخاصية القدرة على الوصول إلى العناصر المخزنة وذلك من خلال الدليل.

الجدول التالي يوضح التوابع المضافة:

اسم الاجراء الوصف

Insert يدخل زوج من (مفتاح\قيمة) في دليل محدد في المجمع.

RemoveAt يحذف زوج (مفتاح\قيمة) من دليل محدد في المجمع.

المثال التالي يوضح كيفية استحدام هذا الصف:

OrderedDictionary od = new OrderedDictionary();

od["Abdulkarim2006@hotmail.com"] = "Kanaan, Abdulkarim";
od["Abdulkarim.Kanaan@gmail.com"] = "Kanaan, Abdulkarim";

foreach (DictionaryEntry d in od)
{
	Console.WriteLine("{0} = {1}", d.Key, d.Value);
}

وشكرا لكم

Ch4L3ListDictionary.zip

هذا الموضوع مغلق.

مواضيع مشابهة