Ma

Maps در Go

2 تمرین

درباره‌ی 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 map هم می‌گوییم):

  var foo map[string]int

برای مقداردهی اولیه‌ی یک map، می‌توانید این‌گونه عمل کنید:

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

یا

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

یک nil map با map مقداردهی‌شده تفاوت دارد؛ نوشتن در یک nil map باعث خطای زمان اجرا می‌شود

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 شما (برای نمونه، ۰ برای یک int) خودش مقداری معتبر باشد. برای بررسی اینکه کلیدی در 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 مقایسه کنیم؟ خب، مشخصات برای این مورد استثنا قائل شده است؛ به مشخصات قابل‌مقایسه نگاه کنید.

آخرین نکته: اگر بخواهید از چند goroutine در یک map بنویسید، آشکارساز رقابت فعال می‌شود؛ به مستندات Go و وبلاگ Go نگاه کنید. در عوض، می‌توانید از sync.Map یا atomic یا mutex برای دور زدن این مشکل استفاده کنید.

ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

Maps را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین Maps قفل 3 تمرین دیگر را باز کنید