Go-তে map একটি বিল্ট-ইন ডেটা টাইপ, যা হ্যাশ টেবিল প্রকাশ করে।
অন্য প্রোগ্রামিং ভাষায় আপনি map-কে dict বা অ্যাসোসিয়েটিভ অ্যারে বা কী (key)/ভ্যালু স্টোর হিসেবে চিনে থাকতে পারেন।
এই ধারণাটির সাথে যদি আপনি পরিচিত না হন, তাহলে map-কে একটি অভিধানের মতো ভাবতে পারেন, যেখানে প্রতিটি শব্দ হলো key আর তার সংজ্ঞা হলো map-এর element।
শুরু করার আগে,
mapসম্পর্কে আরও গভীরে যেতে আমি আপনাকে map-এর Go স্পেসিফিকেশন এবং map-এর Go ব্লগ দেখে নিতে বলব।
সিনট্যাক্সের দিক থেকে map দেখতে এমন:
map[KeyType]ElementType
KeyType অবশ্যই যেকোনো তুলনাযোগ্য টাইপ হতে হবে, আর ElementType Go-এর যেকোনো বৈধ টাইপ হতে পারে, অর্থাৎ আপনি প্রিমিটিভ ভ্যারিয়েবল থেকে স্লাইস পর্যন্ত যেকোনো কিছু সংরক্ষণ করতে পারেন।
এটা মনে রাখা জরুরি যে Go-তে map আনঅর্ডারড। আপনি যদি কোনো map-এর মধ্য দিয়ে লুপ চালিয়ে এলিমেন্টগুলো প্রিন্ট করার চেষ্টা করেন, তাহলে নিজেই অবাক হয়ে দেখবেন যে এলিমেন্টগুলো এলোমেলো ক্রমে ছাপা হচ্ছে (চাইলে নিজে চেষ্টা করে দেখুন)।
এটাও জানা জরুরি যে প্রতিটি কী (key) অনন্য, অর্থাৎ একই কী (key) দুবার অ্যাসাইন করলে সংশ্লিষ্ট কী (key)-এর মান ওভাররাইট হয়ে যাবে।
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")
আপনি যদি এমন কোনো কী (key)-এর মান বের করার চেষ্টা করেন যা map-এ নেই, তাহলে এটি ওই ভ্যালু টাইপের শূন্য মান রিটার্ন করবে।
এতে আপনি বিভ্রান্ত হতে পারেন, বিশেষ করে যদি আপনার ElementType-এর ডিফল্ট মান (যেমন একটি int-এর জন্য 0) একটি বৈধ মান হয়।
আপনার map-এ কোনো কী (key) আছে কি না তা যাচাই করতে আপনি ব্যবহার করতে পারেন
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 Docs এবং The Go Blog।
বিকল্পভাবে, এই সমস্যা এড়াতে আপনি sync.Map বা atomic বা mutex ব্যবহার করতে পারেন।