النوع std::map في C++: القواميس
يعتبر النوع std::map في C++ واحداً من أنواع البيانات المتقدمة التي توفرها مكتبة القوالب القياسية (STL) في C++. يسمح هذا النوع بتخزين مجموعات من الأزواج المفتاحية (key-value) حيث كل مفتاح (key) يكون فريدًا، ويقترن بقيمة (value) مرتبطة به. تُستخدم هذه الهياكل البيانات في العديد من التطبيقات، بدءًا من قواعد البيانات وصولًا إلى الخوارزميات المعقدة التي تتطلب عمليات بحث سريعة ومنظمة.
هيكل std::map
في لغة C++، يعد std::map نوعًا من القواميس الذي يوفر خوارزميات مرتبة وفعّالة للبحث، الإدراج، والحذف. يتم تخزين البيانات في هيكل شجري (عادةً ما يكون شجرة بحث ثنائية متوازنة)، ما يضمن أن العمليات مثل البحث، الإضافة، والحذف تتم في وقت O(logn)، حيث n هو عدد العناصر في الخريطة.
بنية البيانات الأساسية
كل عنصر في std::map هو زوج مكون من مفتاح وقيمة، ويُرمز له عادة بالصورة التالية:
cppstd::map
حيث KeyType هو نوع المفتاح وValueType هو نوع القيمة المرتبطة بالمفتاح. يُخزن العنصر في std::map بشكل مرتب بناءً على قيمة المفتاح، لذا تكون جميع المفاتيح في الخريطة مُرتبة بترتيب تصاعدي افتراضي أو باستخدام دالة مقارنة مخصصة إذا لزم الأمر.
كيفية استخدام std::map
لاستعمال std::map، يجب أولاً تضمين رأس المكتبة المناسبة:
cpp#include
وبعد ذلك يمكن تعريف خريطة باستخدام نوع المفتاح والقيمة المحددة. على سبيل المثال، إذا كنت ترغب في تخزين أزواج من الأسماء والعمر، يمكنك استخدام الشيفرة التالية:
cppstd::mapint> ageMap;
في هذه الحالة، std::string هو نوع المفتاح (الاسم) وint هو نوع القيمة (العمر).
إضافة عناصر إلى std::map
لإضافة عنصر إلى std::map، يمكن استخدام المشغل [] أو دالة insert().
-
استخدام المشغل
[]: عند استخدام هذا المشغل، إذا لم يكن المفتاح موجودًا بالفعل، سيتم إنشاء عنصر جديد تلقائيًا.
cppageMap["Alice"] = 25;
ageMap["Bob"] = 30;
-
استخدام
insert(): تُعتبر هذه الطريقة أكثر أمانًا حيث أنه إذا كان المفتاح موجودًا مسبقًا في الخريطة، فلن يتم إضافة العنصر الجديد.
cppageMap.insert(std::make_pair("Charlie", 35));
الوصول إلى العناصر
يمكن الوصول إلى العناصر في std::map باستخدام المشغل [] أو دالة find().
-
استخدام المشغل
[]: يعمل المشغل[]على إرجاع قيمة مرتبطة بالمفتاح المحدد. إذا كان المفتاح غير موجود، سيتم إضافته مع قيمة مبدئية.
cppint aliceAge = ageMap["Alice"]; // يعطي 25
-
استخدام
find(): توفر هذه الدالة طريقة أكثر أمانًا للوصول إلى العناصر، لأنها لا تضيف أي عنصر جديد إذا كان المفتاح غير موجود.
cppauto it = ageMap.find("Bob");
if (it != ageMap.end()) {
std::cout << "Bob's age is " << it->second << std::endl;
}
حذف العناصر
لحذف عنصر من std::map، يمكن استخدام دالة erase(). هذه الدالة تقبل إما المفتاح أو مؤشر إلى العنصر الذي يجب حذفه.
cppageMap.erase("Alice"); // يحذف العنصر الذي يحتوي على المفتاح "Alice"
التكرار عبر العناصر
يمكن التكرار عبر عناصر std::map باستخدام مؤشرات أو الحلقات مثل for:
cppfor (auto it = ageMap.begin(); it != ageMap.end(); ++it) {
std::cout << it->first << " is " << it->second << " years old" << std::endl;
}
أو باستخدام النطاق المدي (range-based) في C++11 وما بعده:
cppfor (const auto& pair : ageMap) {
std::cout << pair.first << " is " << pair.second << " years old" << std::endl;
}
مقارنة std::map مع std::unordered_map
من المهم ملاحظة الفرق بين std::map و std::unordered_map، وهما نوعان شائعان لتخزين أزواج من المفاتيح والقيم في C++.
-
std::map: يعتمد على شجرة بحث ثنائية متوازنة (مثل شجرة AVL أو Red-Black Tree)، مما يضمن ترتيب العناصر وفقًا للمفاتيح. تعمل العمليات الأساسية (البحث، الإدراج، الحذف) في وقت O(logn). -
std::unordered_map: يعتمد على الجدول المبعثر (hash table)، مما يوفر عمليات بحث، إدراج وحذف في وقت O(1) في المتوسط. لا يتم ترتيب العناصر فيstd::unordered_map.
الاختيار بين هذين النوعين يعتمد على الحاجة إلى ترتيب العناصر في std::map أو السرعة في عمليات الوصول في std::unordered_map.
التعامل مع المفاتيح المكررة
في std::map، لا يمكن أن يكون هناك مفتاح مكرر. إذا حاولت إدراج مفتاح موجود بالفعل، فسيتم تجاهل إدخالك ولن يحدث تغيير في القيمة المرتبطة به. على سبيل المثال:
cppageMap["Bob"] = 30;
ageMap["Bob"] = 40; // القيمة القديمة تُستبدل بالقيمة الجديدة
ولكن، إذا أردت التعامل مع المفاتيح المكررة بطريقة معينة (على سبيل المثال، بتخزين قائمة من القيم لكل مفتاح)، فيمكنك استخدام std::multimap أو هياكل بيانات أخرى مثل std::vector.
ترتيب المفاتيح في std::map
بحسب تصميم std::map، يتم ترتيب المفاتيح في الخريطة بترتيب تصاعدي افتراضي. إذا أردت ترتيب المفاتيح بترتيب مخصص، يمكنك استخدام دالة مقارنة (comparator) عند تعريف الخريطة:
cppstd::map<int, std::string, std::greater<int>> reverseMap;
reverseMap[3] = "three";
reverseMap[1] = "one";
reverseMap[2] = "two";
في هذه الحالة، سيتم ترتيب المفاتيح بترتيب تنازلي.
فوائد std::map
-
الترتيب التلقائي: يسمح
std::mapبتخزين المفاتيح في ترتيب معين (افتراضيًا تصاعدي)، ما يسهل عمليات البحث والاسترجاع. -
البحث السريع: توفر العمليات الأساسية في
std::mapوقت تنفيذ O(logn) بفضل الشجرة الثنائية المتوازنة. -
الأمان: يوفر
std::mapطريقة أكثر أمانًا للوصول إلى العناصر مقارنةً بـstd::unordered_mapلأن ترتيب العناصر محفوظ، ما يعني أن العمليات قد تكون أكثر قابلية للتنبؤ.
الحالات التي يُفضل فيها استخدام std::map
-
عندما تحتاج إلى ترتيب العناصر: إذا كان لديك حاجة لتنظيم البيانات بشكل معين (مثلاً، ترتيب الكلمات حسب تكرارها في نص ما).
-
البحث السريع: عند الحاجة إلى إجراء عمليات بحث بسرعة عالية، مثل البحث عن مفتاح في بيانات ضخمة.
-
الذاكرة الموجهة: عند التعامل مع البيانات المرتبطة بمفاتيح فريدة، مثل قواميس أو خرائط لربط المفتاح بالقيمة في التطبيقات المختلفة.
الختام
النوع std::map في C++ يعد أداة قوية ومرنة لتخزين الأزواج المفتاحية (key-value)، حيث يوفر ميزات متعددة مثل البحث السريع، الترتيب التلقائي للعناصر، وأداء جيد في معظم العمليات. نظرًا لأنه يعتمد على هيكل شجري متوازن، فإنه يضمن عمليات فعّالة في معظم الحالات. وعلى الرغم من توفر أنواع أخرى مثل std::unordered_map التي توفر أداء أفضل في بعض الحالات، يظل std::map الخيار المثالي عندما يكون الترتيب والتدرج مهمين في تطبيقاتك.

