Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Карты (maps) представляют собой ассоциативные массивы, которые связывают ключи с соответствующими значениями. Работа с картами включает получение и запись значений, и Go предоставляет удобный синтаксис для этих операций. Рассмотрим особенности синтаксиса получения и записи значений в карты.
Для этого используется синтаксис индексирования
value := myMap[key]
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value := myMap["Alice"]
fmt.Println("Alice:", value) // Alice: 25
}
Для этого используется синтаксис двойного присваивания
value, exists := myMap[key]
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value, exists := myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found")
}
}
Для этого используется синтаксис индексирования:
myMap[key] = value
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{}
// Добавление значений
myMap["Alice"] = 25
myMap["Bob"] = 30
fmt.Println("Map:", myMap) // Map: map[Alice:25 Bob:30]
// Обновление значения
myMap["Alice"] = 26
fmt.Println("Updated Map:", myMap) // Updated Map: map[Alice:26 Bob:30]
}
Для этого используется встроенная функция
delete
:delete(myMap, key)
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
// Удаление значения по ключу
delete(myMap, "Alice")
fmt.Println("Map after deletion:", myMap) // Map after deletion: map[Bob:30]
}
Если ключ отсутствует в карте, при попытке получения значения будет возвращено нулевое значение типа значения карты. Например, для карты
map[string]int
это будет 0
.package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
}
value := myMap["Bob"] // Ключ "Bob" отсутствует
fmt.Println("Value:", value) // Value: 0
}
Нельзя записывать значения в
nil
карту. Это приведет к панике времени выполнения.package main
func main() {
var myMap map[string]int // nil карта
myMap["Alice"] = 25 // Вызовет панику: runtime error: assignment to entry in nil map
}
Порядок итерации по карте не определен и может различаться между разными запусками программы.
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 35,
}
for key, value := range myMap {
fmt.Printf("%s: %d\n", key, value)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Work stealing — это стратегия параллельной обработки задач, при которой неактивный поток берёт задачу у перегруженного потока.
Это помогает равномерно распределять нагрузку и повышает эффективность многопоточности. Используется в современных планировщиках, в том числе в языках с горутинами и акторной моделью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Может быть как безопасным, так и небезопасным, в зависимости от контекста и конкретного использования.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := make([]byte, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
buffer[i] = byte(i) // Несколько горутин одновременно модифицируют буфер
}(i)
}
wg.Wait()
fmt.Println("Buffer:", buffer)
}
Если несколько горутин только читают из буфера, это безопасно. Пример безопасного использования:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")
for i := 0; i < len(buffer); i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("buffer[%d]: %c\n", i, buffer[i])
}(i)
}
wg.Wait()
}
Для синхронизации доступа к буферу используйте мьютексы (
sync.Mutex
). Это гарантирует, что только одна горутина в данный момент модифицирует буфер.package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
buffer := make([]byte, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mu.Lock()
buffer[i] = byte(i)
mu.Unlock()
}(i)
}
wg.Wait()
fmt.Println("Buffer:", buffer)
}
Если каждая горутина должна работать с независимой копией буфера, создавайте копии для каждой горутины.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
localBuffer := make([]byte, len(buffer))
copy(localBuffer, buffer)
localBuffer[i] = byte(i)
fmt.Printf("Goroutine %d: %s\n", i, localBuffer)
}(i)
}
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Всё зависит от типа числа:
- int, uint — обычно 8 байт на 64-битных системах.
- int32, uint32 — 4 байта.
- int64, uint64 — 8 байт.
- float32 — 4 байта, float64 — 8 байт.
Go явно различает размеры, что позволяет эффективно управлять памятью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Каналы — это мощные инструменты для обмена данными между горутинами, обеспечивающие синхронизацию и безопасную коммуникацию. Основное различие между буферизированными и небуферизированными каналами заключается в их поведении при отправке и получении данных.
Не имеют внутренней емкости, т.е. они не могут хранить значения. Эти каналы требуют, чтобы отправитель и получатель были готовы обмениваться данными одновременно. Если одна сторона не готова, другая будет заблокирована:
Отправка данных в него блокирует отправителя до тех пор, пока получатель не прочитает данные из канала.
Получение данных из него блокирует получателя до тех пор, пока другая горутина не отправит данные в канал.
ch := make(chan int) // Создание небуферизированного канала
go func() {
val := <-ch // Блокируется, ожидая данные
fmt.Println("Received:", val)
}()
ch <- 3 // Блокируется, пока данные не будут получены
Имеют внутреннюю емкость, что позволяет хранить одно или несколько элементов без непосредственного получателя данных. Отправка или получение данных работает следующим образом:
Отправка блокируется, только если буфер заполнен. До этого момента данные могут быть отправлены без блокировки, даже если получатель не готов их принять.
Получение из буферизированного канала блокируется, только если канал пуст. Если в канале есть данные, получение происходит без блокировки.
ch := make(chan int, 2) // Создание буферизированного канала с емкостью 2
ch <- 1 // Отправка данных без блокировки
ch <- 2 // Отправка данных без блокировки
go func() {
val := <-ch // Получение данных без блокировки
fmt.Println("Received:", val)
}()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Да, JOIN можно использовать со вложенными (subquery) запросами. Варианты:
- JOIN с подзапросом, возвращающим таблицу (SELECT ... FROM (SELECT ...) AS subquery JOIN ...).
- JOIN с подзапросом в ON (SELECT ... FROM table1 JOIN (SELECT ...) AS subquery ON ...).
- Использование подзапроса в WHERE или IN, но это менее эффективно, чем JOIN.
Вложенные запросы могут снижать производительность, поэтому лучше использовать индексы и анализировать EXPLAIN.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это функции, которые принимают набор значений и возвращают одно агрегированное значение. В языке Go нет встроенных агрегатных функций, как в SQL, но их можно реализовать самостоятельно.
суммирует все элементы.
вычисляет среднее значение.
находит минимальный элемент.
находит максимальный элемент.
считает количество элементов.
Функция суммы (
SUM
) func Sum(nums []int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
Функция среднего (
AVG
) func Average(nums []int) float64 {
if len(nums) == 0 {
return 0
}
return float64(Sum(nums)) / float64(len(nums))
}
Функция минимума (
MIN
) func Min(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
min := nums[0]
for _, num := range nums {
if num < min {
min = num
}
}
return min
}
Функция максимума (
MAX
) func Max(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
max := nums[0]
for _, num := range nums {
if num > max {
max = num
}
}
return max
}
Функция подсчёта (
COUNT
) func Count(nums []int) int {
return len(nums)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Компилятору не нужно сообщать это явно – соответствие интерфейсу проверяется автоматически. Однако для явной декларации можно использовать конструкцию вида var _ InterfaceName = (*StructName)(nil).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это целочисленные значения, которые используются для доступа к элементам упорядоченных структур данных. В контексте Go индексы чаще всего применяются для работы со строками, массивами, срезами, а также картами (косвенно, через ключи).
Индексы позволяют обращаться к конкретным элементам массива, строки или среза. Например, если у нас есть массив чисел, индекс указывает, какой именно элемент извлечь.
С помощью индексов можно перебирать элементы массива, строки или среза, например, используя циклы.
В изменяемых структурах данных, таких как срезы или массивы, индекс позволяет присвоить новое значение конкретному элементу.
Индексы упрощают и ускоряют доступ к данным, потому что доступ осуществляется за O(1) (константное время) в массивах или срезах.
В строках индексы используются для доступа к конкретным байтам.
package main
import "fmt"
func main() {
str := "Привет"
fmt.Println(str[0]) // 208 (байт, не символ!)
fmt.Printf("%c\n", str[0]) // П (символ, представленный первым байтом UTF-8)
}
В массивах и срезах индексы используются для извлечения и изменения значений
package main
import "fmt"
func main() {
arr := [5]int{10, 20, 30, 40, 50}
fmt.Println(arr[2]) // 30
// Изменение значения по индексу
arr[2] = 100
fmt.Println(arr) // [10 20 100 40 50]
}
Обычно индексы используются для итерации по элементам коллекции с помощью цикла
for
.package main
import "fmt"
func main() {
nums := []int{10, 20, 30, 40, 50}
for i, v := range nums {
fmt.Printf("Индекс: %d, Значение: %d\n", i, v)
}
}
Индексы полезны для извлечения подстрок с использованием срезов:
package main
import "fmt"
func main() {
str := "Привет, Мир!"
fmt.Println(str[8:12]) // Мир
}
Если попытаться обратиться к элементу по индексу, который выходит за пределы коллекции, Go выдаст runtime panic:
package main
func main() {
nums := []int{1, 2, 3}
fmt.Println(nums[5]) // panic: runtime error: index out of range
}
Если неверно учитывать байтовое представление символов UTF-8, можно получить некорректный результат.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1. Primary Index – создается автоматически на первичном ключе таблицы.
2. Unique Index – предотвращает дублирование значений в колонке.
3. Composite Index (составной индекс) – индекс на несколько столбцов.
4. Full-Text Index – используется для быстрого поиска по тексту.
5. Spatial Index – индекс для геоданных (только MyISAM).
6. Clustered Index – хранит строки в отсортированном порядке (InnoDB).
7. Non-Clustered Index – указывает на строки без изменения порядка хранения.
8. Hash Index – используется в MEMORY таблицах, обеспечивает быстрый доступ к данным.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Prometheus – это мощная система мониторинга с временными рядами (time series), которая собирает метрики из сервисов, хранит их и позволяет строить графики и отправлять алерты.
Pull-модель – сам запрашивает метрики у сервисов (в отличие от push-модели, как в StatsD).
Формат временных рядов – каждая метрика привязана ко времени и меткам (
labels
). Язык запросов PromQL – позволяет анализировать и агрегировать метрики.
Автодетектирование сервисов – поддержка Kubernetes, Docker, Consul.
Хранение данных в базе TSDB (Time Series Database).
Гибкая система алертов – интеграция с Alertmanager (уведомления в Slack, Telegram и др.).
Экспортеры/сервисы предоставляют метрики через HTTP-эндпоинт (
/metrics
). Prometheus сам запрашивает данные по расписанию.
Метрики хранятся в базе TSDB.
Можно строить графики в Grafana или запрашивать данные через API.
Алерты отправляются в Alertmanager при достижении пороговых значений.
Go-сервис может отдавать метрики через HTTP с помощью prometheus/client_golang
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// Создаём метрику
var httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
})
func main() {
// Регистрируем метрику
prometheus.MustRegister(httpRequests)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc() // Увеличиваем счётчик при каждом запросе
w.Write([]byte("Hello, Prometheus!"))
})
// Эндпоинт для сбора метрик
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
Пример запроса в PromQL
http_requests_total
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Если у метода ресивер с *, это означает, что метод работает с указателем на объект, а значит:
- изменения внутри метода повлияют на оригинальный объект;
- метод может модифицировать поля структуры;
- вызов возможен как на указателе, так и на значении (Go сам "разыменует").
Такой метод можно вызывать и на value, и на pointer — Go сделает автоматическую конвертацию.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Builder – это порождающий паттерн, который используется для пошагового создания сложных объектов. Он удобен, когда объект имеет много параметров и различных конфигураций.
если у объекта много параметров (особенно опциональных).
вместо длинного конструктора с кучей аргументов можно вызывать методы-построители.
Builder позволяет создать объект инициализированным сразу, без изменения его полей после создания.
В Go нет классов, но можно использовать структуры и методы для реализации этого паттерна.
package main
import "fmt"
// Определяем структуру Car
type Car struct {
Brand string
Model string
Color string
Engine string
}
// Определяем "Строителя" для Car
type CarBuilder struct {
car Car
}
// Методы для пошаговой настройки машины
func (cb *CarBuilder) SetBrand(brand string) *CarBuilder {
cb.car.Brand = brand
return cb
}
func (cb *CarBuilder) SetModel(model string) *CarBuilder {
cb.car.Model = model
return cb
}
func (cb *CarBuilder) SetColor(color string) *CarBuilder {
cb.car.Color = color
return cb
}
func (cb *CarBuilder) SetEngine(engine string) *CarBuilder {
cb.car.Engine = engine
return cb
}
// Метод для финальной сборки объекта
func (cb *CarBuilder) Build() Car {
return cb.car
}
// Используем Builder
func main() {
car := CarBuilder{}.
SetBrand("Tesla").
SetModel("Model S").
SetColor("Red").
SetEngine("Electric").
Build()
fmt.Printf("Car: %+v\n", car)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
456+ реальных собеседований на программиста, тестировщика, аналитика и прочие IT профы.
Есть собесы от ведущих компаний: Сбер, Яндекс, ВТБ, Тинькофф, Озон, Wildberries и т.д.
🎯 Переходи по ссылке и присоединяйся к базе, чтобы прокачать свои шансы на успешное трудоустройство!
Please open Telegram to view this post
VIEW IN TELEGRAM
- Если переменная используется только внутри функции, она остаётся на стеке.
- Если ссылка на переменную передаётся за пределы функции (например, через замыкание), переменная переносится в кучу (heap).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM