تم إعداد ترجمة المقال خصيصًا لطلاب دورة Golang Developer ، والتي تبدأ اليوم فصولها!
في البداية ، من السهل إدراك الصفائف والشرائح على أنها واحدة ومتشابهة ، ولكن بأسماء مختلفة: كلاهما عبارة عن بنية بيانات لتمثيل المجموعات. ومع ذلك ، في الواقع أنها مختلفة جدا عن بعضها البعض.
في هذه المقالة ، نلقي نظرة على الاختلافات والتطبيقات في Go.
سنتحول إلى أمثلة حتى تتمكن من اتخاذ قرار أكثر استنارة حول مكان تطبيقها.
المصفوفات
مجموعة هي مجموعة من حجم ثابت. يتم التركيز هنا على حجم ثابت ، لأنه بمجرد تعيين طول الصفيف ، لن تتمكن بعد ذلك من تغييره.
لنلقِ نظرة على مثال. سننشئ مجموعة من أربع قيم صحيحة:
arr := [4]int{3, 2, 5, 4}
طول ونوع
في المثال أعلاه ،
arr
تعريف متغير
arr
على أنه صفيف من النوع
[4]int
، مما يعني أن الصفيف يتكون من أربعة عناصر. من المهم ملاحظة أن الحجم
4
مدرج في تعريف النوع.
ويترتب على ذلك ، في الواقع ، أن المصفوفات ذات أطوال مختلفة هي صفائف من أنواع مختلفة. لا يمكنك مساواة صفائف أطوال مختلفة مع بعضها البعض ولا يمكنك تعيين قيمة صفيف واحد إلى آخر في هذه الحالة:
longerArr := [5]int{5, 7, 1, 2, 0} longerArr = arr
لقد وجدت أنه من السهل التحدث عن المصفوفات من حيث الهياكل. إذا حاولت إنشاء
بنية مشابهة للصفيف ، فستحصل على الأرجح على ما يلي:
في الواقع ، هذا غير مستحسن ، لكن هذه طريقة جيدة للتعرف على سبب كون المصفوفات ذات الأطوال المختلفة صفائف من أنواع مختلفة.
تمثيل الذاكرة
يتم تخزين الصفيف كسلسلة من كتل
n
من نوع معين:

يتم تخصيص هذه الذاكرة عند تهيئة متغير صفيف.
تمر عبر الرابط
لا يوجد لدى Go شيء مثل المرور بالرجوع إليه ؛ بدلاً من ذلك ، يتم تمرير كل شيء حسب القيمة. إذا قمت بتعيين قيمة المصفوفة لمتغير آخر ، فسيتم ببساطة نسخ القيمة المخصصة.

إذا كنت تريد تمرير "مرجع" فقط إلى المصفوفة ، فاستخدم المؤشرات:

عند تخصيص الذاكرة وفي إحدى الوظائف ، تعد المصفوفة بالفعل نوع بيانات بسيط وتعمل بنفس الطريقة التي تعمل بها الهياكل.
شرائح
يمكن اعتبار الشرائح بمثابة تطبيق موسع للصفائف.
تم تنفيذ الشرائح في Go لتغطية بعض حالات الاستخدام الشائعة للغاية التي يواجهها مطورو البرامج عند العمل مع المجموعات ، مثل تغيير حجم المجموعات ديناميكيًا.
يشبه إعلان الشريحة إلى حد كبير إعلان الصفيف ، باستثناء حذف محدد الطول:
slice := []int{4, 5, 3}
إذا نظرت للتو إلى الكود ، يبدو أن الشرائح والمصفوفات متشابهة تمامًا ، لكن الاختلاف الرئيسي يكمن في التنفيذ وشروط الاستخدام.
تمثيل الذاكرة
يتم تخصيص شريحة بشكل مختلف عن الصفيف ، وهي في الأساس مؤشر معدل. تحتوي كل شريحة على ثلاث مجموعات من المعلومات:
- مؤشر إلى سلسلة من البيانات.
- الطول ، الذي يحدد عدد العناصر الموجودة حاليًا في الشريحة.
- السعة (السعة) ، والتي تحدد العدد الإجمالي لخلايا الذاكرة المتوفرة.

ويترتب على ذلك أنه يمكن تخصيص شرائح بأطوال مختلفة لبعضها البعض. هم من نفس النوع ، ويمكن أن يختلف المؤشر والطول والحجم:
slice1 := []int{6, 1, 2} slice2 := []int{9, 3}
شريحة ، بخلاف مجموعة ، لا تخصص الذاكرة أثناء التهيئة. في الواقع ، تتم تهيئة الشرائح بقيمة
nil
.
تمر عبر الرابط
عندما تقوم بتعيين شريحة لمتغير آخر ، فإنك لا تزال تتجاوز القيمة. هنا ، تشير القيمة فقط إلى المؤشر والطول والحجم ، وليس إلى الذاكرة التي تشغلها العناصر نفسها.

إضافة عناصر جديدة
لإضافة عناصر جديدة إلى الشريحة ، يجب عليك استخدام وظيفة
append
.
nums := []int{8, 0} nums = append(nums, 8)
تحت الغطاء ، سيبدو تخصيص قيمة محددة لعنصر جديد ، وبعد ذلك - إرجاع شريحة جديدة. سيكون طول الشريحة الجديدة واحدًا آخر.

في حالة زيادة الطول بمقدار عنصر واحد عند إضافة عنصر وبالتالي تجاوز الحجم المعلن ، يكون من الضروري توفير وحدة تخزين جديدة (في هذه الحالة ، يتضاعف الحجم الحالي عادة).
لهذا السبب يوصى غالبًا بإنشاء شريحة ذات الطول والحجم المحددين مقدمًا (خاصة إذا كان لديك فكرة واضحة عن حجم الشريحة التي تحتاجها):
arr := make([]int, 0, 5)
ماذا تستخدم: صفائف أو شرائح؟
المصفوفات والشرائح هي أشياء مختلفة تمامًا ، وبالتالي تختلف حالات استخدامها أيضًا.
دعنا ننظر إلى بعض الأمثلة مفتوحة المصدر ومكتبة Go القياسية لفهم ما يجب استخدامه ومتى.
الحالة 1: UUID
UUIDs هي 128 بت من البيانات التي يتم استخدامها غالبًا لتمييز كائن أو كيان. عادةً ما يتم تمثيلها كقيم ست عشرية ، مفصولة شرطات:
e39bdaf4-710d-42ea-a29b-58c368b0c53c
في مكتبة
Google UUID ، يتم تمثيل UUID كصفيف من 16 بايت:
type UUID [16]byte
هذا منطقي لأننا نعرف أن UUID يتكون من 128 بت (16 بايت). لن نقوم بإضافة أو إزالة أي بايت من UUID ، وبالتالي سيكون استخدام الصفيف لتمثيلها.
الحالة 2: فرز قيم عدد صحيح
في هذا المثال ، سوف نستخدم الدالة
sort.Ints
من
مكتبة الفرز القياسية :
s := []int{5, 2, 6, 3, 1, 4}
تأخذ الدالة
sort.Ints
شريحة من الأعداد الصحيحة
sort.Ints
بترتيب تصاعدي للقيم. يفضل استخدام الشرائح هنا لسببين:
- لم يتم تحديد عدد الأعداد الصحيحة (يمكن أن يكون عدد الأعداد الصحيحة للفرز أي) ؛
- يجب فرز الأرقام بترتيب تصاعدي. سيضمن استخدام صفيف تمرير مجموعة الأعداد الصحيحة بأكملها كقيمة ، وبالتالي ستقوم الدالة بترتيب نسختها الخاصة ، وليس المجموعة التي تم تمريرها إليها.
استنتاج
الآن وبعد أن نظرنا في الاختلافات الرئيسية بين المصفوفات والشرائح ، وكذلك حالات استخدامها ، أريد أن أقدم بعض النصائح لتسهيل عليك تحديد التصميم الذي يجب استخدامه:
- إذا تم وصف الكيان بمجموعة من العناصر غير الفارغة ذات الطول الثابت ، فاستخدم الصفائف.
- عند وصف مجموعة تريد إضافة أو حذف عناصر منها ، استخدم الشرائح.
- إذا كان يمكن أن تحتوي المجموعة على أي عدد من العناصر ، فاستخدم الشرائح.
- هل ستغير المجموعة بأي طريقة؟ إذا كان الأمر كذلك ، فينبغي استخدام الشرائح.
كما ترى ، تغطي الشرائح معظم السيناريوهات لإنشاء تطبيقات Go. ومع ذلك ، تتمتع المصفوفات بالحق في الوجود ، وعلاوة على ذلك ، فهي مفيدة بشكل لا يصدق ، لا سيما عندما تظهر حالة استخدام مناسبة.