Golang | Вопросы собесов
4.33K subscribers
26 photos
695 links
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
📌 Как устроен Map в Go?

💬 Спрашивают в 42% собеседований

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

🤔 Внутренняя структура

Map реализуется через хеш-таблицу, что позволяет достигать средней временной сложности операций вставки, поиска и удаления O(1). Вот ключевые компоненты, на которые стоит обратить внимание:

1️⃣ Хеш-функция: Ключ, который вы используете в map, преобразуется с помощью хеш-функции, которая определяет, в каком "bucket" (или "корзине") будет храниться значение. Хеш-функция в Go спроектирована так, чтобы минимизировать коллизии (где разные ключи имеют один и тот же хеш).

2️⃣ Buckets (Корзины): Хеш-таблица разделена на несколько корзин. Каждый бакет может содержать несколько пар ключ-значение, которые имеют один и тот же или близкий хеш. Это помогает организовать данные таким образом, чтобы операции с map были максимально эффективными.

3️⃣ Обработка коллизий: Коллизии в хеш-таблице (когда два ключа дают одинаковый хеш) решаются с помощью метода цепочек, где каждый элемент в бакете содержит указатель на следующий элемент с тем же хешем. Это позволяет нескольким элементам быть связанными в одном бакете.

4️⃣ Рост и перехеширование: По мере того как элементы добавляются в map, количество корзин может увеличиваться для поддержания производительности операций. Когда фактор загрузки (отношение количества элементов к количеству корзин) достигает определенного порога, происходит процесс, называемый перехешированием, в котором элементы распределяются заново среди нового, большего количества корзин.

Поскольку map является встроенным типом, его использование не требует специальных библиотек:
m := make(map[string]int) // Создание map
m["apple"] = 5 // Добавление элемента
m["banana"] = 10 // Добавление другого элемента

value, exists := m["apple"] // Проверка существования ключа и получение значения
if exists {
fmt.Println("Value:", value)
}

delete(m, "apple") // Удаление элемента


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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как объявить новую переменную в Go?
Anonymous Quiz
2%
let
95%
var
1%
dim
2%
new
📌 Как строки работают в Golang внутри?

💬 Спрашивают в 42% собеседований

Строки являются основным типом данных и представляют собой неизменяемые последовательности байт. Понимание того, как строки устроены внутри, поможет лучше управлять производительностью и памятью при работе со строками. Вот основные аспекты внутреннего устройства строк:

1️⃣ Неизменяемость

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

2️⃣ Структура строки

Внутренне строка представлена структурой, которая содержит два поля:

Указатель на массив байтов: Это указатель на первый элемент массива байт, который фактически хранит символы строки в кодировке UTF-8.

Длина: Количество байт в строке, а не количество рун или символов. Это важное различие, поскольку в UTF-8 один символ может занимать от 1 до 4 байт.

3️⃣ UTF-8 как стандартная кодировка

Go использует UTF-8 как стандартную кодировку для строк. Это позволяет эффективно работать с международным текстом, поддерживая широкий спектр символов без сложностей, связанных с другими кодировками. Однако это также означает, что операции, такие как получение длины строки в рунах (символах) или доступ к отдельному символу, могут потребовать дополнительных вычислений для обработки многобайтовых символов.

4️⃣ Срезы строк

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

5️⃣ Производительность и память

Благодаря неизменяемости и способу хранения строк в виде срезов байтов, Go обеспечивает эффективное управление памятью и производительность при работе со строками. Однако необходимо быть осторожным с операциями, которые могут казаться невинными, но приводят к частому созданию новых строк, так как это может повлечь за собой издержки на выделение памяти и сборку мусора.

Вот простой пример демонстрирующий работу со строками:
s := "Hello, world"      // Создание строки
t := s[7:] // Срез строки, создает новую строку "world"

fmt.Println(s) // Выводит: Hello, world
fmt.Println(t) // Выводит: world


Строки — это эффективные и безопасные с точки зрения типов структуры данных, оптимизированные для работы с текстом в кодировке UTF-8. Их неизменяемость и структура с указателем и длиной делают их одновременно быстрыми в обработке и безопасными при передаче между различными частями программы.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой пакет используется для форматированного ввода-вывода в Go?
Anonymous Quiz
2%
input
19%
io
73%
fmt
6%
scan
Зачем аppend возвращает слайс ?
Спросят с вероятностью 8%

Функция append в Go возвращает слайс, потому что она может изменить расположение данных в памяти. Сейчас разберем, почему это происходит и как это влияет на работу со слайсами.

Почему он возвращает слайс

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

2️⃣Изменение базового массива:
Если емкость слайса увеличивается, то базовый массив, на который указывает слайс, изменяется.
Возвращая новый слайс, функция append гарантирует, что вы работаете с актуальной версией слайса, которая указывает на правильный базовый массив.

Демонстрация поведения append
package main

import "fmt"

func main() {
slice := make([]int, 2, 2) // Длина 2, емкость 2
slice[0] = 1
slice[1] = 2

fmt.Println("Before append:", slice) // [1 2]

// Добавляем элемент
slice = append(slice, 3)

fmt.Println("After append:", slice) // [1 2 3]

// Показать емкость после добавления
fmt.Println("Capacity after append:", cap(slice)) // Capacity может быть больше 3, в зависимости от стратегии роста

// Показать новый базовый массив
fmt.Printf("Slice data pointer: %p\n", &slice[0])
}


Здесь:
Изначально слайс slice имеет длину 2 и емкость 2.
После вызова append емкость слайса увеличивается, и слайс указывает на новый базовый массив.

Возврат нового слайса

Поскольку базовый массив может измениться, важно присвоить результат функции append обратно слайсу. Если этого не сделать, можно продолжить использовать старый слайс, который указывает на уже неактуальный массив данных.
// Правильное использование append
slice = append(slice, 4)


Функция append возвращает слайс, чтобы учесть возможное изменение базового массива при увеличении емкости. Это позволяет функции гарантировать, что вы всегда работаете с актуальной версией слайса, даже если базовый массив был перераспределен.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
📌 В чем разница процесса и потока в рамках операционной системы?

💬 Спрашивают в 33% собеседований


Процессы и потоки — это фундаментальные концепции, используемые в операционных системах для управления выполнением программ. Хотя они тесно связаны, между ними есть ключевые различия:

🤔 Процесс

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

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

🤔 Поток (Thread)

Это более легковесная единица выполнения, которая существует внутри процесса. Все потоки внутри одного процесса делят одно и то же адресное пространство памяти и системные ресурсы, такие как файловые дескрипторы. Это позволяет потокам более эффективно общаться друг с другом, поскольку они могут обмениваться данными без использования специальных механизмов межпроцессного взаимодействия.

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

🤔 Основные различия

1️⃣ Изоляция: Процессы изолированы друг от друга, в то время как потоки делят состояние и ресурсы внутри одного процесса.

2️⃣ Память: Каждый процесс имеет собственное адресное пространство, в то время как все потоки внутри процесса делят его адресное пространство.

3️⃣ Создание и управление: Создание нового процесса более ресурсоемко, чем создание потока внутри существующего процесса.

4️⃣ Взаимодействие: Взаимодействие между процессами требует использования межпроцессного взаимодействия (IPC), такого как сокеты, разделяемая память, очереди сообщений и т. д. Потоки внутри процесса могут общаться друг с другом напрямую через общую память.

5️⃣ Надежность и безопасность: Ошибка в одном процессе обычно не влияет на другие процессы, но ошибка в одном потоке может привести к сбою всего процесса.

🤔 Примеры использования

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

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

Выбор между использованием процессов и потоков зависит от требований к производительности, изоляции, безопасности и архитектуры приложения.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой интерфейс определяет стандартные методы для строк и срезов в Go?
Anonymous Quiz
29%
io.Reader
61%
fmt.Stringer
2%
error
8%
fmt.Scanner
Что будет в Map, если не делать make или short assign ?
Спросят с вероятностью 8%

Карты (maps) являются ссылочными типами, и перед использованием их необходимо инициализировать. Если вы попытаетесь использовать карту без предварительной инициализации с помощью make или короткого объявления :=, то это приведет к ошибке выполнения (runtime panic).

Что произойдет при неинициализированной карте

Если вы объявите карту, но не инициализируете ее, она будет иметь значение nil. Попытка вставить элемент в такую карту вызовет панику.
package main
import "fmt"

func main() {
var m map[string]int

// Попытка вставить элемент в неинициализированную карту
m["key"] = 42 // Паника: runtime error: assignment to entry in nil map
}


В этом коде переменная m объявлена как карта, но не инициализирована. Попытка присвоить значение ключу "key" вызовет панику.

Правильная инициализация карты

Чтобы избежать ошибок выполнения, нужно инициализировать карту с помощью функции make или с помощью короткого объявления.
package main
import "fmt"

func main() {
m := make(map[string]int)

// Теперь можно безопасно вставлять элементы
m["key"] = 42

fmt.Println(m["key"]) // Выводит: 42
}


Функция make выделяет память для карты и возвращает ее инициализированную ссылку.

Использование короткого объявления :=
package main

import "fmt"

func main() {
// Инициализация карты с помощью короткого объявления
m := map[string]int{}

// Теперь можно безопасно вставлять элементы
m["key"] = 42

fmt.Println(m["key"]) // Выводит: 42
}


1⃣Неинициализированная карта: Если карта объявлена, но не инициализирована, она имеет значение nil. Попытка вставить элемент вызовет панику.
2⃣Инициализация карты: Карты в Go необходимо инициализировать с помощью make или короткого объявления :=.
3⃣Использование карты: После инициализации карты можно безопасно вставлять элементы, выполнять поиск, удаление и другие операции.

Если не инициализировать карту с помощью make или короткого объявления, она будет иметь значение nil, и попытка вставить в нее элемент вызовет ошибку выполнения (runtime panic).

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
📌 Как работает Select?

💬 Спрашивают в 33% собеседований

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

🤔 Основная механика

Конструкция похожа по своей семантике на оператор switch, но предназначена для работы с каналами. Она позволяет программе ожидать множественных операций канала, блокируя выполнение до тех пор, пока один из каналов не станет доступен для выполнения операции (либо отправки, либо получения).

Давайте рассмотрим пример, где select используется для ожидания данных от двух каналов:
func process(ch1, ch2 <-chan int) {
for {
select {
case v1 := <-ch1:
fmt.Println("Received from ch1:", v1)
case v2 := <-ch2:
fmt.Println("Received from ch2:", v2)
}
}
}


В этом примере функция process будет ожидать данные из двух каналов: ch1 и ch2. Как только один из этих каналов отправит данные, соответствующий case будет выполнен.

🤔 Обработка таймаутов

Одной из мощных возможностей select является возможность обработки таймаутов, что особенно полезно в сетевом программировании или при работе с внешними ресурсами. Вот как это можно сделать:
select {
case v := <-someChannel:
fmt.Println("Received:", v)
case <-time.After(5 * time.Second):
fmt.Println("Timeout occurred, no data received within 5 seconds")
}


Здесь, если данные по каналу someChannel не поступают в течение 5 секунд, будет выполнен второй case, который обрабатывает таймаут.

🤔 Особенности

1️⃣ Неблокирующий вариант: Можно использовать select для неблокирующего чтения или записи в канал, добавив default случай, который выполнится, если все другие каналы заблокированы:
      select {
case v := <-ch:
fmt.Println("Received", v)
default:
fmt.Println("No data received")
}


2️⃣ Равноправие случаев: Если несколько каналов готовы к выполнению операции, select случайным образом выберет один из них для выполнения, обеспечивая тем самым справедливость распределения ресурсов.

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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое ключевое слово используется для управления доступом к ресурсу в многопоточных программах Go?
Anonymous Quiz
29%
sync
56%
mutex
12%
lock
3%
semaphore
В чем отличие длины и ёмкости у слайса ?
Спросят с вероятностью 8%

Слайсы обладают двумя важными характеристиками: длиной (length) и емкостью (capacity). Понимание этих характеристик помогает эффективно использовать слайсы и управлять их памятью. Рассмотрим, что такое длина и емкость слайса, в чем их различия, и как они влияют на работу слайсов.

Длина (Lenght)

Это количество элементов, которые в данный момент находятся в слайсе. Она указывает, сколько элементов можно безопасно прочитать или записать, используя индексацию.
package main

import "fmt"

func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Length:", len(slice)) // Length: 5
}


Емкость (Capacity)

Это количество элементов, которые слайс может содержать без выделения дополнительной памяти. Она определяет максимальное количество элементов, которые могут быть добавлены в слайс до его расширения.
package main

import "fmt"

func main() {
slice := make([]int, 3, 5)
fmt.Println("Length:", len(slice)) // Length: 3
fmt.Println("Capacity:", cap(slice)) // Capacity: 5
}


Различия:

1️⃣Длина:
Количество элементов, доступных в данный момент.
Определяется с помощью функции len.
При обращении к элементам с индексом от 0 до len(slice)-1 гарантируется безопасность.

2️⃣Емкость:
Максимальное количество элементов, которые могут быть добавлены в слайс без выделения новой памяти.
Определяется с помощью функции cap.
Может быть больше или равна длине.

Взаимосвязь длины и емкости

При создании слайса можно задать его длину и емкость. Если емкость не указана, она будет равна длине.
package main

import "fmt"

func main() {
// Слайс с длиной 3 и емкостью 5
slice := make([]int, 3, 5)
fmt.Println("Length:", len(slice)) // Length: 3
fmt.Println("Capacity:", cap(slice)) // Capacity: 5

// Добавление элементов в слайс
slice = append(slice, 4, 5)
fmt.Println("Length after append:", len(slice)) // Length after append: 5
fmt.Println("Capacity after append:", cap(slice)) // Capacity after append: 5

// Добавление еще одного элемента приведет к увеличению емкости
slice = append(slice, 6)
fmt.Println("Length after another append:", len(slice)) // Length after another append: 6
fmt.Println("Capacity after another append:", cap(slice)) // Capacity after another append: 10 (емкость удвоилась)
}


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
📌 Как устроены контексты в Go?

💬 Спрашивают в 33% собеседований

Контексты (context.Context) представляют собой интерфейс, который используется для передачи мета-данных, управления сроками действия и отменой операций в иерархии вызовов функций. Основная цель контекста — обеспечение способа для остановки выполнения программы (например, запросов или подпроцессов) по требованию. Это особенно полезно в сетевых приложениях, где вам может потребоваться прервать выполнение операции, которая больше не требуется или занимает слишком много времени.

Ключевые особенности:

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

2️⃣ Отмена операций: Контекст может быть отменен, что автоматически сигнализирует всем получателям этого контекста о необходимости остановить свою работу. Это обеспечивается через канал, который закрывается, когда контекст отменяется.

3️⃣ Сроки выполнения: Контекст может иметь установленный таймаут или дедлайн, после достижения которого он автоматически отменяется.

4️⃣ Значения: Контексты могут нести значения — пары ключ-значение, которые можно устанавливать и получать. Эти значения обычно используются для передачи данных, специфичных для запроса, таких как идентификаторы сессий или токены авторизации.

🤔 Создание и использование контекста

Есть несколько способов создания контекста, включая базовые функции из пакета context:

context.Background(): Возвращает пустой контекст, который никогда не отменяется. Обычно используется в основной функции и при инициализации.

context.TODO(): Используется для указания, что должен быть предоставлен подходящий контекст. Обычно применяется в разработке и при рефакторинге.

context.WithCancel(parent Context) (ctx Context, cancel CancelFunc): Создает новый контекст с возможностью отмены.

context.WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc): Создает контекст, который автоматически отменяется в указанный deadline.

context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc): Аналогичен WithDeadline, но устанавливает время жизни контекста на основе заданного таймаута.

🤔 Пример:
func operation1(ctx context.Context) {
select {
case <-time.After(5 * time.Second):
fmt.Println("operation1 completed")
case <-ctx.Done():
fmt.Println("operation1 cancelled")
}
}

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

go operation1(ctx)

// Дожидаемся завершения или отмены операции
<-ctx.Done()
if err := ctx.Err(); err != nil {
fmt.Println("main:", err)
}
}


В этом примере функция operation1 прерывается, если контекст отменяется до истечения времени ожидания в 5 секунд.

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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как в Go реализуется шаблон «производитель-потребитель»?
Anonymous Quiz
10%
через map
7%
через mutex
82%
через канал
1%
через slice
📌 Зачем нужны транзакции?

💬 Спрашивают в 33% собеседований

Транзакции — это критически важный механизм в управлении базами данных, предоставляющий способ группировки нескольких операций в одно целостное действие, которое либо полностью выполнится, либо не выполнится вовсе. Это позволяет обеспечить надёжность и последовательность данных даже в условиях многопользовательского доступа и потенциальных системных сбоев.

🤔 Основные принципы:

Они базируются на четырёх основных принципах, известных как свойства ACID:

1️⃣ Атомарность (Atomicity): Гарантирует, что все операции в рамках одной транзакции либо полностью выполнены, либо не выполнены вовсе. Если какая-либо часть транзакции не может быть завершена, вся транзакция откатывается (возвращается к исходному состоянию).

2️⃣ Согласованность (Consistency): Обеспечивает, что транзакция переводит базу данных из одного согласованного состояния в другое. При этом сохраняются все правила и ограничения базы данных.

3️⃣ Изолированность (Isolation): Позволяет транзакциям выполняться независимо друг от друга, изолируя их промежуточные состояния и управляя видимостью изменений данных других транзакций.

4️⃣ Долговечность (Durability): Гарантирует, что результаты успешно завершённой транзакции будут устойчивы к системным сбоям, сохраняясь в постоянной памяти.

🤔 Зачем они нужны?

1️⃣ Целостность данных: Транзакции обеспечивают целостность данных, автоматически управляя ошибками и несогласованностями, которые могут возникнуть в процессе выполнения операций. Например, при переводе средств между банковскими счетами транзакция гарантирует, что если с одного счёта деньги были сняты, то они будут зачислены на другой счёт.

2️⃣ Обработка ошибок: В случае ошибок во время выполнения транзакции (таких как сбои оборудования или исключения в программном обеспечении) система управления базой данных может автоматически откатить все изменения, возвращая данные в исходное состояние, что предотвращает потерю или неправильное распределение данных.

3️⃣ Конкурентный доступ: Транзакции позволяют множеству пользователей работать с базой данных одновременно, при этом минимизируя проблемы, которые могут возникнуть при одновременном доступе к одним и тем же данным, такие как аномалии чтения, потерянные обновления и временные несогласованности.

4️⃣ Восстановление после сбоев: Системы управления базами данных используют журналирование транзакций и чекпоинты для обеспечения возможности восстановления после сбоев, возвращая базу данных в последнее согласованное состояние.

🤔 Пример

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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой оператор используется для безопасного извлечения значений из map в Go?
Anonymous Quiz
24%
get
18%
value
49%
ok
9%
exist
📌 Какие индексы есть?

💬 Спрашивают в 25% собеседований

Индексы — это структуры данных, которые помогают ускорять операции поиска, выборки и сортировки данных, уменьшая при этом количество необходимых чтений с диска. Разные системы управления базами данных поддерживают различные типы индексов, каждый из которых оптимизирован для определённых видов запросов и моделей данных. Вот основные типы индексов, которые обычно используются в реляционных и некоторых нереляционных базах данных:

1️⃣ B-Tree и B+Tree Индексы

Самые распространённые типы индексов, используемые в реляционных базах данных. Они позволяют быстро находить значения в упорядоченном виде, и подходят для большинства операций, включая точечные поиски, диапазонные поиски и сортировку. B+Tree индексы отличаются от B-Tree тем, что все значения хранятся в листовых узлах, что увеличивает эффективность диапазонных запросов.

2️⃣ Хеш-индексы

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

3️⃣ Инвертированные индексы

Часто используются в системах, оптимизированных для поиска текста, например в поисковых движках и некоторых NoSQL базах данных. Эти индексы хранят отображение ключевых слов на места их встречи в базе данных, что позволяет эффективно выполнять текстовый поиск и запросы полнотекстового поиска.

4️⃣ Пространственные (Spatial) индексы

Используются для данных, которые имеют геометрическое представление, такие как точки, линии и полигоны. Они оптимизированы для поиска данных в пространственных запросах, например, при поиске всех точек в определённом радиусе. Распространённые примеры включают R-tree индексы.

5️⃣ Полноценные текстовые индексы

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

6️⃣ Bitmap индексы

Эффективны в особенности для колонок с низкой кардинальностью (т.е. с небольшим числом уникальных значений), таких как пол (мужской/женский) или статус (новый/старый/ремонтируемый). Эти индексы используют битовые карты для быстрого выполнения запросов, фильтрации и агрегации.

🤔 Применение:

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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как в Go создается новый пустой slice заданной длины и емкости?
Anonymous Quiz
87%
make(slice, len, cap)
7%
new(slice, len, cap)
5%
slice(len, cap)
1%
create(slice, len, cap)
Как правильно объявлять Маппу ?
Спросят с вероятностью 8%

Маппы (карты) используются для хранения пар ключ-значение. Они обеспечивают быстрый доступ к данным по ключу и являются очень полезными для различных задач, связанных с ассоциативным хранением данных. Рассмотрим, как правильно объявлять и использовать маппы.

Что это такое?

Это коллекция, где каждый элемент имеет уникальный ключ, по которому можно получить связанное с ним значение. Они имеют тип map[KeyType]ValueType, где KeyType — это тип ключей, а ValueType — тип значений.

Объявление и инициализация мапп

1⃣Используя make:
        var myMap = make(map[string]int)


Здесь myMap — это маппа, где ключи имеют тип string, а значения — тип int.

2⃣Используя литерал маппы:
        myMap := map[string]int{
"apple": 5,
"orange": 10,
}


Этот способ позволяет одновременно объявить и инициализировать маппу с заданными значениями.

Операции с маппами

1⃣Добавление и обновление элементов:
        myMap["banana"] = 3  // добавляет элемент с ключом "banana" и значением 3
myMap["apple"] = 7 // обновляет значение для ключа "apple" на 7


2⃣Чтение значений по ключу:
        appleCount := myMap["apple"]
fmt.Println(appleCount) // выводит 7


3⃣Проверка наличия ключа:
        value, exists := myMap["banana"]
if exists {
fmt.Println("banana count:", value)
} else {
fmt.Println("banana not found")
}


4⃣Удаление элемента:
        delete(myMap, "orange")  // удаляет элемент с ключом "orange"


Рассмотрим пример функции, которая подсчитывает количество слов в строке и возвращает маппу с результатами:
package main
import (
"fmt"
"strings"
)

func wordCount(s string) map[string]int {
words := strings.Fields(s)
countMap := make(map[string]int)
for _, word := range words {
countMap[word]++
}
return countMap
}

func main() {
text := "hello world hello"
result := wordCount(text)
fmt.Println(result) // выводит: map[hello:2 world:1]
}


Маппы — это мощный инструмент для работы с ассоциативными данными. Они позволяют быстро добавлять, обновлять, удалять и искать элементы по ключу.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🤔 Как в Go проверить, является ли канал закрытым?
Anonymous Quiz
3%
if ch.closed
8%
if ch == nil
74%
if _, ok := <-ch; !ok
15%
if ch.closed()
Какие ключи могут быть в МАПе ?
Спросят с вероятностью 8%

Карты (maps) представляют собой хеш-таблицы, где ключи и значения могут быть разных типов. Однако не все типы данных могут использоваться в качестве ключей в карте. Сейчас рассмотрим, какие типы данных можно использовать в качестве ключей, и почему некоторые типы нельзя использовать.

Типы, которые можно использовать в качестве ключей

Они должны быть сравнимыми, поскольку Go использует оператор == для проверки равенства ключей. Это необходимо для корректной работы хеш-таблицы. В качестве ключей можно использовать следующие типы:

1️⃣Базовые типы:
bool
int
, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64, uintptr
float32, float64
complex64, complex128
string

2️⃣Указатели:
Указатели на любые типы (*T).

3️⃣Интерфейсы:
Интерфейсы, при условии, что конкретные значения, которые они содержат, также поддерживают сравнение (==).

4️⃣Структуры:
Структуры, если все их поля поддерживают сравнение (==).

Примеры допустимых ключей:

Использование строковых
package main

import (
"fmt"
)

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
fmt.Println(myMap)
}


Использование целочисленных
package main

import (
"fmt"
)

func main() {
myMap := map[int]string{
1: "one",
2: "two",
}
fmt.Println(myMap)
}


Использование указателей
package main

import (
"fmt"
)

func main() {
a, b := 1, 2
myMap := map[*int]string{
&a: "one",
&b: "two",
}
fmt.Println(myMap)
}


Использование структур
package main

import (
"fmt"
)

type Point struct {
X, Y int
}

func main() {
myMap := map[Point]string{
{X: 1, Y: 2}: "Point A",
{X: 3, Y: 4}: "Point B",
}
fmt.Println(myMap)
}


Типы, которые нельзя использовать в качестве ключей, потому что они не поддерживают оператор ==. К ним относятся:

Слайсы ([]T).
Карты (map[K]V).
Функции (func).

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

Пример недопустимого ключа (слайс)
package main

func main() {
myMap := map[[]int]string{
[]int{1, 2, 3}: "slice",
}
// Это вызовет ошибку компиляции: "invalid map key type []int"
}


Можно использовать только те типы данных в качестве ключей для карт, которые поддерживают оператор сравнения ==. Это включает базовые типы, указатели, интерфейсы (при условии, что конкретные значения сравнимы) и структуры, все поля которых поддерживают сравнение.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых