Удаление из начала слайса требует сдвига всех оставшихся элементов на один шаг влево. Это операция линейной сложности — O(n), где n — количество элементов после удалённого.
В отличие от удаления с конца, которое обычно O(1), начало требует перераспределения памяти или сдвига указателей.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5
Нет, карты (maps) в Go не являются потокобезопасными по умолчанию. Использование карты в нескольких горутинах без должной синхронизации может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим более детально, почему это так и как можно обеспечить потокобезопасность при работе с картами в многопоточной среде.
Карты в Go разработаны для обеспечения высокой производительности при использовании в однопоточных сценариях. Если несколько горутин одновременно пытаются читать и изменять карту, возникает конкуренция за доступ к данным, что может привести к следующим проблемам:
Одновременный доступ к карте из нескольких горутин может привести к состояниям гонки, когда порядок выполнения операций непредсказуем.
Одновременное изменение структуры карты, например добавление или удаление элементов, может вызвать панику в программе.
Самый распространенный способ синхронизации доступа к карте — использование
sync.Mutex
. Мьютексы позволяют заблокировать доступ к карте на время чтения или записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Если в вашей программе чаще происходят операции чтения, чем записи, можно использовать
sync.RWMutex
, который позволяет нескольким горутинам читать данные одновременно, но обеспечивает эксклюзивный доступ для записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.RLock()
defer mu.RUnlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Go предоставляет специальную структуру
sync.Map
, которая изначально создана для безопасного использования в многопоточной среде. Она автоматически обеспечивает синхронизацию операций.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("key1", 42)
}()
go func() {
defer wg.Done()
value, _ := m.Load("key1")
fmt.Println(value)
}()
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Функция append используется для добавления элементов к слайсу. Эта функция может увеличивать длину слайса и, при необходимости, выделять новый подлежащий массив, если текущий массив не имеет достаточной емкости для хранения новых элементов.
Если текущий подлежащий массив слайса имеет достаточно места (емкости) для добавления новых элементов,
append
просто добавляет элементы к существующему массиву.Если емкость текущего массива недостаточна для размещения новых элементов,
append
выделяет новый массив, копирует в него существующие элементы и добавляет новые элементы. Новый массив будет иметь увеличенную емкость (как правило, в два раза больше, чем предыдущая).Синтаксис
slice = append(slice, elem1, elem2, ...)
Пример использования
package main
import "fmt"
func main() {
// Создаем слайс из 3 целых чисел
slice := []int{1, 2, 3}
// Добавляем один элемент
slice = append(slice, 4)
// Добавляем несколько элементов
slice = append(slice, 5, 6, 7)
// Выводим слайс
fmt.Println(slice) // Выводит: [1 2 3 4 5 6 7]
}
Создаем слайс с тремя элементами
[1, 2, 3]
.slice := []int{1, 2, 3}
Добавляем элемент
4
к слайсу. Теперь слайс содержит [1, 2, 3, 4]
.slice = append(slice, 4)
Добавляем элементы
5
, 6
, и 7
. Теперь слайс содержит [1, 2, 3, 4, 5, 6, 7]
.slice = append(slice, 5, 6, 7)
Выводим слайс, который содержит
[1, 2, 3, 4, 5, 6, 7]
.fmt.Println(slice)
Работа с емкостью и длиной
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
fmt.Printf("Before append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 4)
fmt.Printf("After append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 5, 6, 7)
fmt.Printf("After multiple appends: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7👍1
Триггер — это специальная процедура в СУБД, которая автоматически выполняется при определённом событии (например,
INSERT
, UPDATE
, DELETE
) в таблице.Когда выполняется определённое действие с таблицей, триггер срабатывает автоматически и выполняет заданную логику. Это полезно для:
- Автоматической проверки данных.
- Поддержания целостности информации.
- Логирования изменений.
Допустим, у нас есть таблица
orders
, и мы хотим автоматически сохранять историю изменений заказов в таблицу orders_log
.CREATE TABLE orders_log (
id SERIAL PRIMARY KEY,
order_id INT,
old_status TEXT,
new_status TEXT,
changed_at TIMESTAMP DEFAULT now()
);
CREATE OR REPLACE FUNCTION log_order_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO orders_log (order_id, old_status, new_status)
VALUES (OLD.id, OLD.status, NEW.status);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER order_status_change
AFTER UPDATE ON orders
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION log_order_update();
Используется для проверки или модификации данных перед изменением.
Чаще всего используется для логирования или дополнительных действий.
Позволяет заменить выполнение операции (
INSERT
, UPDATE
, DELETE
).Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
1. Допустимые типы:
- Примитивы: int, float, string, bool.
- Указатели.
- Структуры, если все их поля имеют сравнимые типы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Forwarded from easyoffer
Напоминаю, что в честь релиза запускаем акцию.
Первые 500 покупателей получат:
🚀 Скидку 50% на PRO тариф на 1 год
🎁 Подарок ценностью 5000₽ для тех, кто подписан на этот канал
🔔 Подпишитесь на этот канал: https://t.me/+b2fZN17A9OQ3ZmJi
В нем мы опубликуем сообщение о релизе в первую очередь
Please open Telegram to view this post
VIEW IN TELEGRAM
Тип rune представляет собой alias для типа
int32
, предназначенного для хранения Unicode кодовых точек.Строки (
string
) являются последовательностями байтов, а не символов. Это означает, что один символ может занимать больше одного байта, особенно если это символ из расширенного набора Unicode.Тип используется для работы с символами, представляемыми одной кодовой точкой Unicode. Это упрощает манипуляции с символами, так как каждая
rune
— это отдельный символ, независимо от его длины в байтах.Использование типа
rune
делает код более понятным и само-документируемым. Когда в коде виден тип rune
, это сразу указывает на то, что переменная предназначена для хранения одного символа, а не целого числа.Создание и инициализация
var r rune = '世'
fmt.Println(r) // Output: 19990
Итерация по строке
s := "Привет, 世界"
for _, r := range s {
fmt.Printf("%c ", r)
}
// Output: П р и в е т , 世 界
Преобразование между
string
ито такоеs := "Go"
runes := []rune(s)
fmt.Println(runes) // Output: [71 111]
s2 := string(runes)
fmt.Println(s2) // Output: Go
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Массив (array) — это структура данных, которая представляет собой фиксированную последовательность элементов одного типа. Все элементы массива размещены в памяти последовательно и имеют одинаковый тип. Используются для хранения коллекций данных, где количество элементов заранее известно и фиксировано.
Размер массива задается при его объявлении и не может изменяться во время выполнения программы.
Все элементы массива имеют один и тот же тип.
Элементы массива хранятся последовательно в памяти, что обеспечивает быстрый доступ к любому элементу по индексу.
Массивы объявляются с указанием типа элементов и фиксированного размера:
var arr [5]int
Могут быть инициализированы при объявлении:
arr := [5]int{1, 2, 3, 4, 5}
Можно также инициализировать массив частично, оставив остальные элементы равными нулям:
arr := [5]int{1, 2}
Осуществляется с использованием индексов, начиная с 0:
fmt.Println(arr[0]) // 1
arr[1] = 10
fmt.Println(arr[1]) // 10
Фиксирована и задается при его объявлении. Ее можно получить с помощью функции
len
:fmt.Println(len(arr)) // 5
При присваивании одного массива другому копируются все элементы:
arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := arr1
arr2[0] = 10
fmt.Println(arr1) // [1 2 3 4 5]
fmt.Println(arr2) // [10 2 3 4 5]
Копируется весь массив:
func modifyArray(a [5]int) {
a[0] = 10
}
arr := [5]int{1, 2, 3, 4, 5}
modifyArray(arr)
fmt.Println(arr) // [1 2 3 4 5]
С помощью оператора
==
, если они имеют одинаковую длину и тип элементов:arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{4, 5, 6}
fmt.Println(arr1 == arr2) // true
fmt.Println(arr1 == arr3) // false
Пример
package main
import (
"fmt"
)
func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}
// Доступ к элементам
fmt.Println("First element:", arr[0]) // First element: 1
// Изменение элементов
arr[1] = 10
fmt.Println("Modified array:", arr) // Modified array: [1 10 3 4 5]
// Длина массива
fmt.Println("Length of array:", len(arr)) // Length of array: 5
// Копирование массива
arr2 := arr
arr2[0] = 20
fmt.Println("Original array:", arr) // Original array: [1 10 3 4 5]
fmt.Println("Copied array:", arr2) // Copied array: [20 10 3 4 5]
// Передача массива в функцию
modifyArray(arr)
fmt.Println("Array after modifyArray call:", arr) // Array after modifyArray call: [1 10 3 4 5]
}
func modifyArray(a [5]int) {
a[0] = 10
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
HTTP (HyperText Transfer Protocol) — это протокол, используемый для передачи данных в интернете.
Год выпуска: 1991
Особенности
Первая версия HTTP, очень простая. Поддерживала только GET-запросы. Отсутствие заголовков, только один объект мог быть передан в ответ на запрос.
Использование:
В настоящее время практически не используется.
Год выпуска: 1996
Особенности
Введены методы запроса, такие как GET, POST и HEAD. Поддержка заголовков для метаданных. Каждый запрос/ответ требует нового соединения, что делает передачу данных менее эффективной.
Использование
Исторически важен, но также почти не используется в современных приложениях.
Год выпуска: 1997
Особенности
Поддержка устойчивых (persistent) соединений, что позволяет повторное использование одного соединения для нескольких запросов/ответов. Введены дополнительные методы запросов, такие как OPTIONS, PUT, DELETE, TRACE и CONNECT. Поддержка chuncked transfer encoding для передачи данных по частям. Улучшена работа с кэшированием и аутентификацией.
Использование
Широко используется и в настоящее время, хотя многие системы переходят на HTTP/2.
Год выпуска: 2015
Особенности
Бинарный протокол, что улучшает производительность и уменьшает количество ошибок. Поддержка мультиплексирования, что позволяет отправлять несколько запросов через одно соединение одновременно, уменьшая задержки. Сжатие заголовков, что уменьшает объем передаваемых данных. Серверное push-сообщение (server push), позволяющее серверу отправлять данные клиенту до того, как он их запросит.
Использование
Быстро набирает популярность благодаря улучшенной производительности и эффективности.
Год выпуска: 2022 (черновая версия в 2020)
Особенности
Основан на протоколе QUIC, который работает поверх UDP, а не TCP. Поддержка улучшенного мультиплексирования и быстрого установления соединений. Лучшая устойчивость к потерям пакетов и более высокая скорость передачи данных по сравнению с HTTP/2.
Использование
Внедрение продолжается, но многие крупные интернет-компании, такие как Google и Facebook, уже активно используют HTTP/3.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Замыкание (closure) — это функция, которая захватывает переменные из внешней области видимости и продолжает использовать их даже после выхода из этой области.
Замыкание «помнит» окружение, в котором оно было создано, и может работать с ним как с локальным контекстом.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Нет, карты (maps) в Go не являются потокобезопасными по умолчанию. Использование карты в нескольких горутинах без должной синхронизации может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим более детально, почему это так и как можно обеспечить потокобезопасность при работе с картами в многопоточной среде.
Карты в Go разработаны для обеспечения высокой производительности при использовании в однопоточных сценариях. Если несколько горутин одновременно пытаются читать и изменять карту, возникает конкуренция за доступ к данным, что может привести к следующим проблемам:
Одновременный доступ к карте из нескольких горутин может привести к состояниям гонки, когда порядок выполнения операций непредсказуем.
Одновременное изменение структуры карты, например добавление или удаление элементов, может вызвать панику в программе.
Самый распространенный способ синхронизации доступа к карте — использование
sync.Mutex
. Мьютексы позволяют заблокировать доступ к карте на время чтения или записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Если в вашей программе чаще происходят операции чтения, чем записи, можно использовать
sync.RWMutex
, который позволяет нескольким горутинам читать данные одновременно, но обеспечивает эксклюзивный доступ для записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.RLock()
defer mu.RUnlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Go предоставляет специальную структуру
sync.Map
, которая изначально создана для безопасного использования в многопоточной среде. Она автоматически обеспечивает синхронизацию операций.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("key1", 42)
}()
go func() {
defer wg.Done()
value, _ := m.Load("key1")
fmt.Println(value)
}()
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
HTTP/3, работающий на основе QUIC, обеспечивает быструю передачу данных поверх UDP. Для его использования в Go можно применять библиотеки вроде quic-go, позволяющие интегрировать поддержку HTTP/3.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Один из полезных паттернов, который я использовал в реальном проекте на Go — это Worker Pool (пул воркеров).
Этот паттерн помогает ограничить количество горутин при обработке задач, чтобы не перегружать систему.
В моём проекте был сервис, который обрабатывал тысячи файлов параллельно. Если запускать новую горутину на каждый файл, то память быстро заканчивалась, и сервер "умирал".
Решение: вместо создания тысяч горутин я использовал Worker Pool с фиксированным числом воркеров (например, 5), которые брали задачи из очереди.
Есть канал задач (
jobs
). Есть пул воркеров (
workers
), которые читают задачи из канала. Каждый воркер обрабатывает одну задачу и берёт следующую.
package main
import (
"fmt"
"sync"
"time"
)
// Количество воркеров
const workerCount = 5
// Воркер, который берет задачи из канала и обрабатывает их
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job)
time.Sleep(time.Second) // Имитация обработки
}
}
func main() {
jobs := make(chan int, 10)
var wg sync.WaitGroup
// Запускаем воркеров
for i := 1; i <= workerCount; i++ {
wg.Add(1)
go worker(i, jobs, &wg)
}
// Отправляем задачи в канал
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs) // Закрываем канал, чтобы воркеры знали, что задачи кончились
wg.Wait() // Ждём завершения всех воркеров
fmt.Println("Все задачи обработаны")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊2👍1
Оконные функции (window functions) — это SQL-функции, которые выполняются по строкам с учётом контекста «окна» (группы строк).
Примеры использования:
- Нумерация (ROW_NUMBER())
- Скользящие суммы/средние (SUM() OVER(...))
- Сравнение соседних строк (LAG(), LEAD())
- Ранжирование (RANK())
Они позволяют делать аналитику прямо в SQL, без подзапросов и группировок.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM