package
0.0.0-20240731124852-a4f780a8695e
Repository: https://github.com/nixolay/training.git
Documentation: pkg.go.dev

# README

MAP

map - в го представляет из себя как хэш-карту.

Хэш-карта - это классическая структура данных, предлагающая O(1) поисковых запросов в среднем и O(n) в худшем случае. То есть, когда все работает хорошо, время выполнения функции map является почти постоянным.

Размер этой константы является частью дизайна hashmap, и точка, в которой карта перемещается от O (1) до O (n) времени доступа, определяется ее хеш-функцией.

Хэш-функция принимает ключ неизвестной длины и возвращает значение с фиксированной длиной.

hash(key)integer

Классическая хэш-карта представляет собой массив сегментов, каждый из которых содержит указатель на массив записей ключ/значение. В этом случае наша hashmap имеет восемь сегментов и каждый сегмент может содержать до восьми записей каждый. Использование степеней двойки позволяет использовать дешевые битовые маски и сдвиги, а не дорогостоящее разделение.

По мере добавления записей на карту, предполагая хорошее распределение хэш-функции, сегменты будут заполняться примерно с одинаковой скоростью. Как только количество записей в каждом сегменте превысит некоторый процент от их общего размера, известный как коэффициент загрузки, карта будет расти за счет удвоения количества сегментов и перераспределения записей между ними.

Мы видели, что для реализации hashmap необходимо четыре свойства:

  • Для ключа нужна хеш-функция.
  • Для сравнения ключей вам нужна функция равенства.
  • Вам нужно знать размер ключа.
  • Вам нужно знать размер значения, потому что они влияют на размер структуры корзины, которую компилятор должен знать, когда вы переходите или вставляете в эту структуру, как далеко продвигаться в памяти.

map -представляет из себя структуру https://github.com/golang/go/blob/master/src/runtime/map.go#L116

// A header for a Go map.
type hmap struct {
	// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
	// Make sure this stays in sync with the compiler's definition.
	count     int // # live cells == size of map.  Must be first (used by len() builtin)
	flags     uint8
	B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
	noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
	hash0     uint32 // hash seed

	buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
	oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
	nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

	extra *mapextra // optional fields
}

map это указатель на hmap в Go (именно он создается при объявлении с помощью var, но не инициализируется, из-за чего падает программа при попытке вставки). Поле buckets — хранилище пар ключ-значение, таких «ведер» несколько, в каждом лежит 8 пар. Сначала в «ведре» лежат слоты для дополнительных битов хэшей. Далее лежат ключи и значения как сначала список всех ключей, потом список всех значений.

По хэш функции определяется в какое «ведро» мы кладем значение, внутри каждого «ведра» может лежать до 8 коллизий, в конце каждого «ведра» есть указатель на дополнительное, если вдруг предыдущее переполнилось.

Основной функционал работы с map(ой):

// https://github.com/golang/go/blob/master/src/runtime/map.go#L304
m := make(map[string]int)
// https://github.com/golang/go/blob/master/src/runtime/map.go#L395
v := m["key"]     // runtime.mapaccess1(m, ”key", &v)
// https://github.com/golang/go/blob/master/src/runtime/map.go#L456
v, ok := m["key"] // runtime.mapaccess2(m, ”key”, &v, &ok)
// https://github.com/golang/go/blob/master/src/runtime/map.go#L578
m["key"] = 9001   // runtime.mapassign(m, ”key", 9001)
// https://github.com/golang/go/blob/master/src/runtime/map.go#L695
delete(m, "key")  // runtime.mapdelete(m, “key”)

При переборе MAP(ы), мы будем получать разную последовательность:

for k, v := range map[int]int{1:1,2:2,3:3,4:4,5:5,6:6} {
	fmt.Printf("key: %d, value: %t\n", k, v)
}

Причина кроется в рантайме

// https://github.com/golang/go/blob/master/src/runtime/map.go#L847
// mapiterinit initializes the hiter struct used for ranging over maps. 
func mapiterinit(t *maptype, h *hmap, it *hiter) {...
// decide where to start 
r := uintptr(fastrand())
 ... 
it.startBucket = r & bucketMask(h.B)...}

Место поиска определяется рандомно.

Мапа не является защищенной для потоков, используйте обертку sync.Mutex или уже готовую реализацию sync.Map