Ma

Maps في Go

تمرينان

نبذة عن Maps

في Go، map نوع بيانات مدمج يمثّل جدول تجزئة. في لغات برمجة أخرى، ربما تعرف map باسم dict أو المصفوفة الترابطية أو مخزن مفتاح/قيمة. إذا لم تكن على دراية بهذا المفهوم، فيمكنك أن تتخيل map كقاموس، كل كلمة فيه هي key والتعريف هو element الخاص بالـ map.

قبل أن نبدأ، أود أن أوجّهك إلى مواصفات Go حول map ومدونة Go حول map للتعمق أكثر في map.

من الناحية التركيبية، يبدو map هكذا:

map[KeyType]ElementType

يجب أن يكون KeyType أي نوع قابل للمقارنة، بينما يمكن أن يكون ElementType أي نوع صالح في Go، ما يعني أنك تستطيع تخزين أي شيء من متغير أولي إلى شريحة.

من المهم أن تتذكر أن map في Go غير مرتّبة، فإذا حاولت المرور بحلقة على map وطباعة العنصر، فقد تفاجئك رؤية عناصرك مطبوعة بترتيب عشوائي (جرّب ذلك إن أردت).

من المهم أيضًا أن تعرف أن كل مفتاح فريد، أي أن إسناد المفتاح نفسه مرتين سيستبدل قيمة المفتاح المقابل.

map نوع مرجعي، وهذا يعني أنك إذا مرّرته هنا وهناك، فلن تنسخ Go الـ map كاملة. ما ستفعله Go بدلًا من ذلك هو نسخ مؤشر الـ map، وهذا يجعل تمرير الـ map إلى دالة أو متغير غير مكلف. قيمة الـ map غير المهيأة هي nil.

يمكنك تعريف map كما يلي (ونسمي هذه أيضًا خريطة nil):

  var foo map[string]int

لتهيئة map، يمكنك أن تفعل:

  // With map literal
  foo := map[string]int{}

أو

  // or with make function
  foo := make(map[string]int)

الخريطة nil مختلفة عن الخريطة المهيأة، والكتابة في خريطة nil تسبب خطأً وقت التشغيل

panic: assignment to entry in nil map

لذلك من المهم تهيئة الـ map قبل استخدامها

إليك بعض العمليات التي يمكنك إجراؤها على map

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

إذا حاولت استرداد قيمة مفتاح غير موجود في الـ map، فستُرجع القيمة الصفرية لنوع القيمة. قد يربكك هذا، خصوصًا إذا كانت القيمة الافتراضية لنوع ElementType لديك (مثلًا، 0 لعدد صحيح) قيمة صالحة. للتحقق من وجود مفتاح في الـ map، يمكنك استخدام

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

كما رأينا سابقًا، معرفة ما إذا كانت الـ map مهيأة أم لا أمر سهل، يمكننا ببساطة مقارنتها بـ nil، لكن ماذا عن المقارنة بين map وأخرى؟ حسنًا، بما أن map ليست قابلة للمقارنة، لا يمكننا استخدام عامل ==، لكن حزمة reflect لديها ما ينقذنا، وهو DeepEqual، ويمكننا استخدامه هكذا

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

لكن انتظر، إذا لم تكن map قابلة للمقارنة، فلماذا نستطيع مقارنتها بـ nil؟ حسنًا، لقد وضعت المواصفة استثناءً لهذا، انظر مواصفات المقارنة

النقطة الأخيرة: إذا حاولت الكتابة في map من عدة goroutines، فسيؤدي ذلك إلى تشغيل كاشف التسابق، انظر وثائق Go ومدونة Go. وبدلًا من ذلك، يمكنك استخدام sync.Map أو atomic أو mutex للالتفاف على هذه المشكلة.

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم Maps

التدريب مقفل

افتح 3 تمارين إضافية لممارسة Maps