C++الكلاس unordered_multiset
- تعريف الكلاس
unordered_multiset
- دوال الكلاس
unordered_multiset
- أمثلة شاملة حول التعامل مع الكلاس
unordered_multiset
تعريف الكلاس unordered_multiset
تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11
و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash()
تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه مع الإشارة إلى أنه يمكن تخزين قيم مكررة فيها.
بالمبدأ الدالة Hash()
تقوم بإجراء عملية حسابية على قيم أي عنصر يتم إدخاله.
أي ناتج جديد غير مكرر ترجعه الدالة Hash()
يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash()
بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.
كما أنه بإمكانك تعريف الدالة طريقة عمل الدالة Hash()
بنفسك حتى تحدد الطريقة التي سيتم على أساسها إنشاء Buckets و ترتيب العناصر ضمنهم.
على سبيل المثال إذا كنت تنوي إنشاء حاوية خاصة لتخزين الأعداد, يمكنك جعل الدالة Hash()
تنشئ Buckets على حسب عدد الأرقام التي يتألف منها كل عدد. عندها سيتم ترتيب العناصر كالتالي:
- العدد الذي يتألف من رقم واحد مثل
3
-7
سيتم وضعهما في Bucket. - العدد الذي يتألف من رقمين واحد مثل
12
-45
سيتم وضعهما في Bucket. - العدد الذي يتألف من ثلاثة أرقام مثل
100
-273
سيتم وضعهما في Bucket و هكذا..
معلومة تقنية
الفرق الوحيد بين الحاوية التي تنشئها من الكلاس unordered_set
و الحاوية التي تنشئها من الكلاس unordered_multiset
هو أن هذا الأخير يمكنه أن يحتوي على أكثر من عنصر عندهم نفس القيمة.
الآن, بما أن مفاتيح العناصر في هذه الحاوية يمكن أن تكون مكررة فهذا يعني أنه لا يمكنك الإعتماد على قيم المفاتيح للتفرقة بين العناصر حيث أن المفتاح الواحد يمكن أن يرمز لعدة عناصر.
لاستخدام الكلاس unordered_multiset
- أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف لأنه موجود فيه.
بناء الكلاس
template < class Key, // unordered_multiset::key_type/value_type class Hash = hash<Key>, // unordered_multiset::hasher class Pred = equal_to<Key>, // unordered_multiset::key_equal class Alloc = allocator<Key> // unordered_multiset::allocator_type > class unordered_multiset;
إذاً عند إنشاء كائن من الكلاس unordered_multiset
يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر Key
.
دوال الكلاس unordered_multiset
الجدول التالي يحتوي على دوال الكلاس unordered_multiset
التي تستخدم للحصول على عداد يتيح المرور على عناصره.
الدالة مع تعريفها | |
---|---|
1 | iterator begin()
ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر. |
2 | iterator end()
ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر. |
3 | local_iterator begin(size_t n)
ترجع كائن iterator يشير لمكان أول عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد البدأ من أول عنصر موجود فيه. |
4 | local_iterator end(size_t n)
ترجع كائن iterator يشير لمكان آخر عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد الوقوف عند آخر عنصر موجود فيه. |
الجدول التالي يحتوي على دوال الكلاس unordered_multiset
التي تستخدم للحصول على عدد عناصره.
الدالة مع تعريفها | |
---|---|
1 | bool empty()
تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك. |
2 | size_t size()
تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها. |
3 | size_t max_size()
تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها. |
4 | size_t bucket_count()
تستخدم للحصول على عدد الـ Buckets الموجودين في الكائن الذي قام باستدعائها. |
5 | size_t bucket_size(size_t n)
تستخدم للحصول على عدد العناصر الموجودة في Bucket محددة في الكائن الذي قام باستدعائها.مكان الباراميتر n نمرر عدد يمثل Index الـ Bucket التي سيتم إرجاع عدد العناصر الموجودة فيه. |
6 | size_t max_bucket_count()
تستخدم للحصول على أكبر عدد Buckets يمكن تخزينها في الكائن الذي قام باستدعائها. |
7 | size_t bucket(key_type& k)
تستخدم للحصول على رقم Bucket محددة في الكائن الذي قام باستدعائها بالإعتماد على قيمة موجودة فيها.مكان الباراميتر k نمرر قيمة العنصر الذي نريد معرفة في Bucket موجود. |
الجدول التالي يحتوي على دوال الكلاس unordered_multiset
التي تستخدم للتحكم بعناصره.
الدالة مع تعريفها | |
---|---|
1 | void emplace(T& val)
تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها. |
2 | iterator erase(const_iterator position)
تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه. |
3 | iterator erase(const_iterator first, const_iterator last)
تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده. |
4 | void swap(unordered_multiset& anotherSet)
تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.مكان الباراميتر anotherSet نمرر لها كائن من الكلاس unordered_multiset يملك نفس نوع عناصر الكائن الذي قام باستدعائها. |
الجدول التالي يحتوي على دوال الكلاس unordered_multiset
التي تستخدم للبحث فيه.
الدالة مع تعريفها | |
---|---|
1 | size_t count(T& val)
تستخدم للبحث في الكائن الذي قام باستدعائها عن عدد العناصر التي تملك قيمة محددة.مكان الباراميتر val نمرر القيمة التي نريد البحث عنها. |
2 | iterator find(const T& val)
تستخدم للبحث في الكائن الذي قام باستدعائها عن أول عنصر يملك قيمة محددة.مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end() . |
أمثلة شاملة حول التعامل مع الكلاس unordered_multiset
في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مع تحديد أنه يمكن أن يحتوي على عناصر نوعها string
.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة كم Bucket تم إنشاؤها و إجمالي عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.
ملاحظة: قمنا باستخدام الدالة emplace()
لإضافة العناصر, الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size()
لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin()
للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end()
للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.
المثال الأول
using namespace std; int main() { // string يمكنه أن يحتوي على قيم نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس unordered_multiset<string> ums; // emplace() باستخدام الدالة ums هنا قمنا بإضافة 9 عناصر في الكائن ums.emplace("Mon"); ums.emplace("Tue"); ums.emplace("Wed"); ums.emplace("Thu"); ums.emplace("Fri"); ums.emplace("Sat"); ums.emplace("Sun"); ums.emplace("Thu"); // تعمدنا إعادة إدخال هذه القيمة مرة ثانية حتى تلاحظ أنه يمكن إدخال قيم مكررة ums.emplace("Fri"); // تعمدنا إعادة إدخال هذه القيمة مرة ثانية حتى تلاحظ أنه يمكن إدخال قيم مكررة // bucket_count() باستخدام الدالة ums تم إنشاؤها في الكائن Bucket هنا قمنا بطباعة كم cout << "ums total buckets = " << ums.bucket_count() << endl; // size() باستخدام الدالة ums هنا قمنا بطباعة عدد عناصر الكائن cout << "ums total elements = " << ums.size() << endl; cout << "ums values ="; // ums هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن // Bucket وصولاً لآخر عنصر موجود في آخر Bucket إبتداءاً من أول عنصر موجود في أول for (auto it = ums.begin(); it != ums.end(); ++it) { cout << " " << *it; } return 0; }
سنحصل على النتيجة التالية عند التشغيل.
ums total buckets = 17 ums total elements = 9 ums values = Sun Tue Wed Thu Thu Fri Fri Mon Sat
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
ملاحظة: قمنا باستخدام الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size()
لمعرفة عدد العناصر الموجودة في كل Bucket.
المثال الثاني
using namespace std; int main() { // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ int totalBuckets = ums.bucket_count(); // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ for(int i=0; i<totalBuckets; i++) { // Bucket في كل دورة سيتم طباعة رقم الـ cout << "bucket[" << i << "] elements:"; // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية for(auto it=ums.begin(i); it!=ums.end(i); ++it) { cout << " " << *it; } cout << endl; } }
سنحصل على النتيجة التالية عند التشغيل.
bucket[0] elements: Fri bucket[1] elements: bucket[2] elements: Sat Mon bucket[3] elements: bucket[4] elements: Sun Wed bucket[5] elements: bucket[6] elements: bucket[7] elements: Tue bucket[8] elements: bucket[9] elements: bucket[10] elements: Thu
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
ملاحظة: قمنا باستخدام الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size()
لمعرفة عدد العناصر التي تم إضافتها. عند عرض قيم العناصر الموجودة في كل Bucket قمنا باستخدام الدالة begin()
للحصول على مؤشر للعنصر الأول فيها لأننا سنبدأ من عنده و الدالة end()
للحصول على مؤشر للعنصر الأخير فيها لأننا سنتوقف عنده.
المثال الثالث
using namespace std; int main() { // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ int totalBuckets = ums.bucket_count(); // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ for(int i=0; i<totalBuckets; i++) { // و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ cout << "bucket[" << i << "] total elements = " << ums.bucket_size(i); cout << endl; } }
سنحصل على النتيجة التالية عند التشغيل.
bucket[0] total elements = 1 bucket[1] total elements = 0 bucket[2] total elements = 2 bucket[3] total elements = 0 bucket[4] total elements = 2 bucket[5] total elements = 0 bucket[6] total elements = 0 bucket[7] total elements = 1 bucket[8] total elements = 0 bucket[9] total elements = 0 bucket[10] total elements = 1
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيم محددة فيه لمعرفة كم عنصر يملك هذه القيم.
ملاحظة: قمنا باستخدام الدالة count()
لمعرفة كم مرة القيم مكررة في الكائن.
المثال الرابع
using namespace std; int main() { // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس unordered_multiset<string> ums = {"Apple", "Orange", "Apple", "Tomato", "Banana", "Pineapple", "Blueberry"}; // "Apple" التي تملك القيمة ums هنا قمنا بطباعة عدد العناصر الموجودة في الكائن cout << "'Apple' is exist " << ums.count("Apple") << " time(s)\n"; // "Carrot" التي تملك القيمة ums هنا قمنا بطباعة عدد العناصر الموجودة في الكائن cout << "'Carrot' is exist " << ums.count("Carrot") << " time(s)"; return 0; }
سنحصل على النتيجة التالية عند التشغيل.
'Apple' is exist 2 time(s) 'Carrot' is exist 0 time(s)
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن أول عنصر يملك قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.
ملاحظة: قمنا باستخدام الدالة find()
للبحث عن القيمة في الكائن و الدالة erase()
لحذف العنصر من الكائن.
المثال الخامس
using namespace std; int main() { // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه unordered_multiset<string>::iterator منا قمنا بتعريف كائن من unordered_multiset<string>::iterator it; // it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن ums هنا قمنا بالبحث في الكائن // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة ums.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها it = ums.find("Wed"); // فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها ums.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة if (it != ums.end()) { // و من ثم قمنا بحذفه ums هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن cout << "First element with value '"<< *it << "' is removed from ums\n"; ums.erase(it); } cout << "ums values ="; // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن for (auto it = ums.begin(); it != ums.end(); ++it) { cout << " " << *it; } return 0; }
سنحصل على النتيجة التالية عند التشغيل.
First element with value 'Wed' is removed from ums ums values = Fri Thu Sun Tue Sat Mon
في المثال التالي قمنا بتعريف كائنين من unordered_multiset
مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int
.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.
ملاحظة: قمنا باستخدام الدالة swap()
لتبديل قيمهما.
المثال السادس
using namespace std; int main() { // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها unordered_multiset هنا قمنا بتعريف كائنين من الكلاس unordered_multiset<int> ums1 = {1, 2, 3, 4}; unordered_multiset<int> ums2 = {5, 6, 7, 8}; // ums2 مع قيم الكائن ums1 هنا قمنا بتبديل قيم الكائن ums1.swap(ums2); cout << "ums1 values = "; // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن for (auto it = ums1.begin(); it != ums1.end(); ++it) { cout << *it << " "; } cout << "\nums2 values = "; // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن for (auto it = ums2.begin(); it != ums2.end(); ++it) { cout << *it << " "; } return 0; }
سنحصل على النتيجة التالية عند التشغيل.
ums1 values = 8 7 6 5 ums2 values = 4 3 2 1
في المثال التالي قمنا بتعريف كائن من unordered_multiset
مخصص لتخزين قيم نوعها int
مع تحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets و إضافة عدة قيم فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets قمنا بإنشاء كلاس إسمه Hash
و إعادة تعريف العامل ()
حتى يقارن قيمة أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع الأعداد التي تتألف من رقم واحد في Bucket, و الأعداد التي تتألف من رقم رقمين في Bucket, و الأعداد التي تتألف من ثلاثة أعداد في Bucket و هكذا..
في النهاية قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
المثال السابع
using namespace std; // unordered_multiset سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه class Hash { public: // Bucket حتى يقرر في أي unordered_multiset هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس // val سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن القيمة التي يملكها العنصر سيتم تمريرها مكان الباراميتر size_t operator() (const int &val) const { // بشكل مؤقت لأننا نحتاج إجراء تعديل عليها val سيتم وضع قيمة num المتغير // val سنستخدمه لحساب عدد الأرقام الموجودة في المتغير digits المتغير int num = val; int digits = 1; // num و من ثم حذف أول رقم في المتغير digits هنا قمنا بإنشاء حلقة تقوم في كل دورة بإضافة واحد على قيمة المتغيرد // num تمثل عدد الأرقام الموجودة في العدد الذي كان يحتويه المتغير digits عندما تتوقف هذه الحلقة ستكون قيمة المتغير while (num != 0) { digits++; num = num / 10; } // سيتم إضافة العنصر Bucket لأنها التي ستحدد في digits في النهاية سيتم إرجاع قيمة المتغير return digits; } }; int main() { // مع تحديد أنه يعتمد على الكلاس string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس // من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash unordered_multiset<int, Hash> ums = {6, 34, 72, 3, 1, 123, 55, 100, 2020}; // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ int totalBuckets = ums.bucket_count(); // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ for(int i=0; i<totalBuckets; i++) { // Bucket في كل دورة سيتم طباعة رقم الـ cout << "bucket[" << i << "] elements:"; // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية for(auto it=ums.begin(i); it!=ums.end(i); ++it) { cout << " " << *it; } cout << endl; } return 0; }
سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأرقام التي يتألف منها كل عنصر.
bucket[0] elements: bucket[1] elements: 1 3 6 bucket[2] elements: 55 72 34 bucket[3] elements: 100 123 bucket[4] elements: 2020 bucket[5] elements: bucket[6] elements: bucket[7] elements: bucket[8] elements: bucket[9] elements: bucket[10] elements: