Golang | Вопросы собесов
4.34K subscribers
28 photos
700 links
Download Telegram
Как можно слить два слайса ?
Спросят с вероятностью 8%

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

Слияние двух слайсов

Пример 1: Слияние с использованием append
package main

import "fmt"

func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}

// Используем оператор разворачивания `...`, чтобы добавить все элементы slice2 в slice1
mergedSlice := append(slice1, slice2...)

fmt.Println("Slice 1:", slice1) // [1 2 3]
fmt.Println("Slice 2:", slice2) // [4 5 6]
fmt.Println("Merged Slice:", mergedSlice) // [1 2 3 4 5 6]
}


Пояснение

Здесь:
1️⃣Создаются два слайса slice1 и slice2.
2️⃣Используется функция append и оператор разворачивания ..., чтобы добавить все элементы slice2 в slice1.
3️⃣Результатом является новый слайс mergedSlice, содержащий элементы из обоих исходных слайсов.

Пример 2: Слияние строковых слайсов
package main

import "fmt"

func main() {
slice1 := []string{"a", "b", "c"}
slice2 := []string{"d", "e", "f"}

// Слияние строковых слайсов
mergedSlice := append(slice1, slice2...)

fmt.Println("Slice 1:", slice1) // [a b c]
fmt.Println("Slice 2:", slice2) // [d e f]
fmt.Println("Merged Slice:", mergedSlice) // [a b c d e f]
}


Пример с предварительным созданием результирующего слайса

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

Пример 3: Использование copy и append
package main

import "fmt"

func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}

// Создаем результирующий слайс с нужной емкостью
mergedSlice := make([]int, len(slice1) + len(slice2))

// Копируем элементы из первого слайса
copy(mergedSlice, slice1)

// Добавляем элементы из второго слайса
copy(mergedSlice[len(slice1):], slice2)

fmt.Println("Slice 1:", slice1) // [1 2 3]
fmt.Println("Slice 2:", slice2) // [4 5 6]
fmt.Println("Merged Slice:", mergedSlice) // [1 2 3 4 5 6]
}


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

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

🔐 База собесов | 🔐 База тестовых
Что такое интеграционные тесты ?
Спросят с вероятностью 8%

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

Почему они нужны:
1️⃣Проверка взаимодействия компонентов: Убедиться, что различные части системы правильно взаимодействуют друг с другом.
2️⃣Выявление проблем на ранних этапах: Легче и дешевле исправлять ошибки, обнаруженные на этапе тестирования, чем в продакшене.
3️⃣Гарантия целостности системы: Обеспечить, что изменения в одном модуле не сломают другие модули.

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

Рассмотрим простую систему, где есть два модуля: модуль для работы с базой данных и модуль для обработки HTTP-запросов.
// file: main.go
package main

import (
"database/sql"
"fmt"
"log"
"net/http"

)

var db *sql.DB

func main() {
var err error
db, err = sql.Open("sqlite3", "./foo.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()

http.HandleFunc("/user", getUserHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
user, err := getUserByID(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "User: %s", user)
}

func getUserByID(id string) (string, error) {
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", id).Scan(&name)
if err != nil {
return "", err
}
return name, nil
}


Для написания интеграционного теста мы можем использовать библиотеку
net/http/httptest

для тестирования HTTP-запросов и библиотеки для работы с базой данных:
// file: main_test.go
package main

import (
"net/http"
"net/http/httptest"
"testing"

)

func TestGetUserHandler(t *testing.T) {
// Подготовка тестовой базы данных
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
db.Exec("INSERT INTO users (id, name) VALUES (1, 'Alice')")

// Создание запроса
req, err := http.NewRequest("GET", "/user?id=1", nil)
if err != nil {
t.Fatal(err)
}

// Создание ResponseRecorder для записи ответа
rr := httptest.NewRecorder()
handler := http.HandlerFunc(getUserHandler)

// Выполнение запроса
handler.ServeHTTP(rr, req)

// Проверка ответа
expected := "User: Alice"
if rr.Body.String() != expected {
t.Errorf("expected %v, got %v", expected, rr.Body.String())
}
}


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

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

🔐 База собесов | 🔐 База тестовых
Что такое функция copy ?
Спросят с вероятностью 8%

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

Синтаксис функции
func copy(dst, src []T) int


dst: слайс назначения (куда будут копироваться элементы).
src: слайс источника (откуда будут копироваться элементы).

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

Пример 1: Копирование слайса

package main

import "fmt"

func main() {
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 5) // Создаем слайс назначения длиной 5

n := copy(dst, src) // Копируем элементы из src в dst
fmt.Println("Copied elements:", n) // 5
fmt.Println("Destination slice:", dst) // [1 2 3 4 5]
}


Пример 2: Частичное копирование

Если длина слайса назначения меньше длины слайса источника, то копируются только те элементы, которые помещаются в слайс назначения.
package main

import "fmt"

func main() {
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3) // Создаем слайс назначения длиной 3

n := copy(dst, src) // Копируем элементы из src в dst
fmt.Println("Copied elements:", n) // 3
fmt.Println("Destination slice:", dst) // [1 2 3]
}


Пример 3: Копирование в пустой слайс

Если длина слайса источника меньше длины слайса назначения, то копируются только те элементы, которые имеются в источнике.
package main

import "fmt"

func main() {
src := []int{1, 2, 3}
dst := make([]int, 5) // Создаем слайс назначения длиной 5

n := copy(dst, src) // Копируем элементы из src в dst
fmt.Println("Copied elements:", n) // 3
fmt.Println("Destination slice:", dst) // [1 2 3 0 0]
}


Применение функции copy

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

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

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

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

🔐 База собесов | 🔐 База тестовых
Как в Go пишут unit тесты со стандартным пакетом testing ?
Спросят с вероятностью 8%

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

Как это делается:

1️⃣Создание тестового файла: Тестовые файлы должны иметь суффикс _test.go. Например, если у вас есть файл math.go, тестовый файл для него будет называться math_test.go.
2️⃣Написание тестовых функций: Тестовые функции должны начинаться с Test, а их имя должно быть описательным. Например, TestAdd для функции Add.
3️⃣Использование метода `t.Errorf` или t.Fatalf: Эти методы позволяют сообщать о провале теста. t.Errorf продолжает выполнение тестов после ошибки, а t.Fatalf завершает выполнение текущего теста.

Допустим, у нас есть простая функция для сложения двух чисел в файле math.go:
// file: math.go
package math

func Add(a, b int) int {
return a + b
}


Создадим тест для этой функции в файле math_test.go:
// file: math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; expected %d", result, expected)
}
}


Детали:

1️⃣Импорт пакетамер, TestAd Это обязательный шаг для использования функций и типов, предоставляемых пакетом testing.
2️⃣Определение тестовой функции: Функция должна принимать один аргумент типа *testing.T. Это структура, предоставляемая пакетом testing, которая используется для логирования и отчета об ошибках.
3️⃣Проверка результата: Внутри тестовой функции вы вызываете тестируемую функцию, проверяете результат и используете методы t.Errorf или t.Fatalf для сообщения о несоответствиях.

Расширенные возможности:

Табличные тесты: Полезны для тестирования нескольких сценариев с разными входными данными и ожидаемыми результатами.
func TestAddTableDriven(t *testing.T) {
tests := []struct {
a, b int
result int
}{
{1, 1, 2},
{2, 3, 5},
{10, -2, 8},
{-1, -1, -2},
}

for _, tt := range tests {
t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
res := Add(tt.a, tt.b)
if res != tt.result {
t.Errorf("Add(%d, %d) = %d; expected %d", tt.a, tt.b, res, tt.result)
}
})
}
}


Чтобы запустить тесты, используйте команду:
go test


Эта команда автоматически находит все файлы с суффиксом _test.go, компилирует их и запускает тестовые функции.

Модульные тесты пишутся с использованием стандартного пакета testing. Они помогают убедиться, что отдельные функции работают правильно. Тесты создаются в файлах с суффиксом _test.go и включают тестовые функции, начинающиеся с Test, которые проверяют результаты работы функций и сообщают о несоответствиях.

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

🔐 База собесов | 🔐 База тестовых
Что такое моки (mocks) ?
Спросят с вероятностью 8%

Моки (mocks) – это имитации объектов или компонентов, которые используются в тестировании для замены реальных объектов. Они помогают изолировать тестируемый код, эмулируя поведение зависимостей, таких как базы данных, внешние API или другие сервисы. Это особенно полезно в модульных тестах, где важно проверить функциональность конкретного блока кода без влияния внешних факторов.

Зачем они нужны:

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

Как их использовать:

Для создания их часто используются интерфейсы и специальные библиотеки, такие как gomock или testify/mock.

Пример использования с testify/mock:

1️⃣Определение интерфейса:
// file: user.go
package user

type User struct {
ID int
Name string
}

type UserRepository interface {
GetUserByID(id int) (*User, error)
}


2️⃣Создание функции, использующей интерфейс:
// file: service.go
package user

type UserService struct {
Repo UserRepository
}

func (s *UserService) GetUserName(id int) (string, error) {
user, err := s.Repo.GetUserByID(id)
if err != nil {
return "", err
}
return user.Name, nil
}


3️⃣Написание теста с использованием мока:
// file: service_test.go
package user

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

// MockUserRepository is a mock implementation of the UserRepository interface.
type MockUserRepository struct {
mock.Mock
}

func (m *MockUserRepository) GetUserByID(id int) (*User, error) {
args := m.Called(id)
return args.Get(0).(*User), args.Error(1)
}

func TestGetUserName(t *testing.T) {
mockRepo := new(MockUserRepository)
service := UserService{Repo: mockRepo}

// Определяем поведение мока
mockRepo.On("GetUserByID", 1).Return(&User{ID: 1, Name: "Alice"}, nil)

// Выполнение теста
name, err := service.GetUserName(1)

// Проверка результатов
assert.NoError(t, err)
assert.Equal(t, "Alice", name)

// Проверка, что мок был вызван с правильными параметрами
mockRepo.AssertExpectations(t)
}


Объяснение:

1️⃣Определение интерфейса: Интерфейс UserRepository определяет метод GetUserByID.
2️⃣Использование интерфейса в сервисе: Сервис UserService использует UserRepository для получения информации о пользователе.
3️⃣Создание мока: Мы создаем мок MockUserRepository, реализующий интерфейс UserRepository.
4️⃣Определение поведения мока: Устанавливаем ожидания, что метод GetUserByID будет вызван с определенными параметрами и вернет заданные результаты.
5️⃣Тестирование сервиса: Проверяем, что метод GetUserName возвращает правильное значение, и что мок был вызван с ожидаемыми параметрами.

Моки – это заменители реальных объектов, используемые в тестировании для изоляции кода, ускорения тестов и создания предсказуемых условий. Для этого часто используют интерфейсы и библиотеки, такие как testify/mock.

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

🔐 База собесов | 🔐 База тестовых
Как можно добавить элементы в слайс ?
Спросят с вероятностью 8%

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

Использование append для добавления элементов в слайс

1️⃣Добавление одного элемента

Чтобы это сделать используйте append следующим образом:
package main

import "fmt"

func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4) // Добавление одного элемента
fmt.Println(slice) // [1 2 3 4]
}


2️⃣Добавление нескольких элементов

Чтобы это сделать передайте их через запятую:
package main

import "fmt"

func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4, 5, 6) // Добавление нескольких элементов
fmt.Println(slice) // [1 2 3 4 5 6]
}


3️⃣Добавление элементов из другого слайса

Чтобы это сделать используйте оператор разворачивания (...):

package main

import "fmt"

func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
slice1 = append(slice1, slice2...) // Добавление элементов из slice2 в slice1
fmt.Println(slice1) // [1 2 3 4 5 6]
}


Пример динамического увеличения емкости слайса

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

import "fmt"

func main() {
slice := make([]int, 0, 2) // Создаем слайс длиной 0 и емкостью 2
fmt.Println("Initial:", slice, "Len:", len(slice), "Cap:", cap(slice))

// Добавляем элементы
slice = append(slice, 1)
fmt.Println("After append 1:", slice, "Len:", len(slice), "Cap:", cap(slice))

slice = append(slice, 2)
fmt.Println("After append 2:", slice, "Len:", len(slice), "Cap:", cap(slice))

slice = append(slice, 3) // Емкость увеличивается автоматически
fmt.Println("After append 3:", slice, "Len:", len(slice), "Cap:", cap(slice))

slice = append(slice, 4, 5, 6) // Добавляем несколько элементов
fmt.Println("After append 4, 5, 6:", slice, "Len:", len(slice), "Cap:", cap(slice))
}


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

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

🔐 База собесов | 🔐 База тестовых
🤔 Какое ключевое слово используется для создания нового типа в Go?
Anonymous Quiz
2%
define
15%
struct
74%
type
9%
new
Опишите алгоритм, как будет происходить вставка в Map ?
Спросят с вероятностью 8%

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

1️⃣Хеширование ключа: Вычисляется хеш-код для ключа с использованием функции хеширования. Это дает нам число, которое будет использоваться для определения позиции ключа в хеш-таблице.

2️⃣Определение индекса: Индекс в массиве корзин (buckets) вычисляется на основе хеш-кода и размера массива. Обычно используется операция побитового И (&) для получения индекса.

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

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

5️⃣Вставка элемента: Новый элемент добавляется в соответствующую корзину.

Алгоритм вставки на более низком уровне:

1️⃣Хеширование ключа:
        hash := hashFunction(key)


2️⃣Определение индекса:
        index := hash & (len(buckets) - 1)


3️⃣Поиск корзины и проверка на коллизию:
Проверяем корзину по индексу:
            bucket := buckets[index]

Итерируемся по элементам в корзине для поиска совпадения ключа:
            for _, element := range bucket {
if element.key == key {
element.value = newValue
return
}
}


4️⃣Добавление нового элемента:
Если совпадения ключа не найдено, добавляем новый элемент в корзину:
            bucket = append(bucket, newElement)


Пример вставки элемента в карту:
package main

import "fmt"

func main() {
// Создаем карту
myMap := make(map[string]int)

// Вставка элементов
myMap["Alice"] = 25
myMap["Bob"] = 30

// Обновление значения существующего ключа
myMap["Alice"] = 26

// Печать карты
fmt.Println(myMap)
}


В этом примере:
1️⃣Создается карта myMap с ключами типа string и значениями типа int.
2️⃣Вставляются пары ключ-значение: "Alice": 25 и "Bob": 30.
3️⃣Значение для ключа "Alice" обновляется на 26.
4️⃣Карта выводится на экран.

Важные моменты:

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

Вставка в карту включает хеширование ключа, определение индекса корзины, проверку на коллизии и добавление элемента в соответствующую корзину. Если ключ уже существует, его значение обновляется.

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

🔐 База собесов | 🔐 База тестовых
🤔 Какой из методов используется для добавления нового элемента в карту (map) в Go?
Anonymous Quiz
32%
append()
3%
insert()
8%
add()
56%
Присваивание по ключу
Как проверить слайс на пустоту ?
Спросят с вероятностью 8%

Существует несколько способов проверки слайса на пустоту. Выбор метода зависит от конкретной ситуации и того, что именно вы подразумеваете под "пустотой". Рассмотрим различные подходы.

1️⃣Проверка длины слайса

Самый распространенный способ проверить, пустой ли слайс, — это проверить его длину с помощью функции len. Если длина слайса равна нулю, то слайс пустой.
package main

import "fmt"

func main() {
var slice1 []int // nil слайс
slice2 := []int{} // Пустой слайс, но не nil
slice3 := []int{1, 2, 3} // Непустой слайс

fmt.Println("slice1 is empty:", len(slice1) == 0) // true
fmt.Println("slice2 is empty:", len(slice2) == 0) // true
fmt.Println("slice3 is empty:", len(slice3) == 0) // false
}


2️⃣Проверка на nil

Иногда важно отличать nil слайс от пустого слайса с нулевой длиной. Для этого можно использовать сравнение с ним же.
package main

import "fmt"

func main() {
var slice1 []int // nil слайс
slice2 := []int{} // Пустой слайс, но не nil

fmt.Println("slice1 is nil:", slice1 == nil) // true
fmt.Println("slice2 is nil:", slice2 == nil) // false
}


Комбинированная проверка

В некоторых случаях может потребоваться отличать nil слайс от пустого слайса и одновременно проверять, пустой ли он. Это можно сделать с помощью комбинации проверок len и сравнения с nil.
package main

import "fmt"

func isEmpty(slice []int) bool {
return slice == nil || len(slice) == 0
}

func main() {
var slice1 []int // nil слайс
slice2 := []int{} // Пустой слайс, но не nil
slice3 := []int{1, 2, 3} // Непустой слайс

fmt.Println("slice1 is empty:", isEmpty(slice1)) // true
fmt.Println("slice2 is empty:", isEmpty(slice2)) // true
fmt.Println("slice3 is empty:", isEmpty(slice3)) // false
}


Для проверки слайса на пустоту обычно используют len(slice) == 0. Для проверки, является ли слайс nil, используют slice == nil. Для комбинированной проверки можно использовать оба условия.

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

🔐 База собесов | 🔐 База тестовых
Что такое хэш-коллизия ?
Спросят с вероятностью 8%

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

Почему они возникают:

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

Методы разрешения:

1️⃣Открытая адресация:
Линейное пробирование: При коллизии алгоритм проверяет следующую ячейку в массиве до тех пор, пока не найдет пустую ячейку.
          index = (hash + i) % len(buckets)

Квадратичное пробирование: Вместо линейного шага проверяются ячейки с квадратичным шагом.
          index = (hash + i*i) % len(buckets)

Двойное хеширование: Используются две разные хэш-функции для расчета шага пробирования.
          index = (hash1 + i*hash2) % len(buckets)


2️⃣Цепочки (Chaining):
Каждая корзина (bucket) является связным списком или другой структурой данных, в которой хранятся все элементы с одинаковым хэш-значением.
          type Node struct {
key string
value int
next *Node
}


Пример реализации цепочек:
package main

import (
"container/list"
"fmt"
)

// Простая хэш-таблица с цепочками
type HashTable struct {
buckets []*list.List
size int
}

// Создаем новую хэш-таблицу
func NewHashTable(size int) *HashTable {
buckets := make([]*list.List, size)
for i := range buckets {
buckets[i] = list.New()
}
return &HashTable{
buckets: buckets,
size: size,
}
}

// Хэш-функция
func (ht *HashTable) hash(key string) int {
hash := 0
for i := 0; i < len(key); i++ {
hash = (31 * hash) + int(key[i])
}
return hash % ht.size
}

// Вставка в хэш-таблицу
func (ht *HashTable) Insert(key string, value int) {
index := ht.hash(key)
bucket := ht.buckets[index]
for e := bucket.Front(); e != nil; e = e.Next() {
pair := e.Value.(map[string]int)
if _, ok := pair[key]; ok {
pair[key] = value
return
}
}
bucket.PushBack(map[string]int{key: value})
}

// Поиск в хэш-таблице
func (ht *HashTable) Search(key string) (int, bool) {
index := ht.hash(key)
bucket := ht.buckets[index]
for e := bucket.Front(); e != nil; e = e.Next() {
pair := e.Value.(map[string]int)
if val, ok := pair[key]; ok {
return val, true
}
}
return 0, false
}

func main() {
ht := NewHashTable(10)
ht.Insert("Alice", 25)
ht.Insert("Bob", 30)
ht.Insert("Charlie", 35)

if value, found := ht.Search("Alice"); found {
fmt.Println("Alice:", value)
} else {
fmt.Println("Alice not found")
}

if value, found := ht.Search("Bob"); found {
fmt.Println("Bob:", value)
} else {
fmt.Println("Bob not found")
}

if value, found := ht.Search("Eve"); found {
fmt.Println("Eve:", value)
} else {
fmt.Println("Eve not found")
}
}


Объяснение кода:
HashTable: Структура, представляющая хэш-таблицу с цепочками.
NewHashTable: Функция, создающая новую хэш-таблицу заданного размера.
hash: Простая хэш-функция для строк.
Insert: Метод для вставки ключ-значение в хэш-таблицу.
Search: Метод для поиска значения по ключу в хэш-таблице.

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

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

🔐 База собесов | 🔐 База тестовых
Можно ли добавлять элементы в nil слайс ?
Спросят с вероятностью 8%

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

Пример:
package main

import "fmt"

func main() {
var nilSlice []int // nil слайс

// Добавление элемента в nil слайс
nilSlice = append(nilSlice, 1)
fmt.Println(nilSlice) // [1]
fmt.Println(len(nilSlice)) // 1
fmt.Println(cap(nilSlice)) // 1
fmt.Println(nilSlice == nil) // false (теперь слайс больше не nil)

// Добавление нескольких элементов
nilSlice = append(nilSlice, 2, 3, 4)
fmt.Println(nilSlice) // [1 2 3 4]
fmt.Println(len(nilSlice)) // 4
fmt.Println(cap(nilSlice)) // в зависимости от реализации, емкость может быть увеличена
}


Как он работает с nil слайсом

Когда вызывается append для nil слайса, происходит следующее:

1️⃣Проверка емкости: Go проверяет, достаточно ли емкости для добавления новых элементов.
2️⃣Выделение памяти: Если емкости недостаточно (что всегда будет в случае nil слайса), Go выделяет новый массив с достаточной емкостью для хранения существующих и новых элементов.
3️⃣Копирование элементов: Существующие элементы (если таковые имеются) копируются в новый массив.
4️⃣Добавление новых элементов: Новые элементы добавляются в массив.
5️⃣Возврат нового слайса: Возвращается новый слайс, который указывает на новый массив.
package main

import "fmt"

func main() {
var nilSlice []int

// Проверка начальных значений
fmt.Println("Initial nilSlice:", nilSlice) // []
fmt.Println("Length:", len(nilSlice)) // 0
fmt.Println("Capacity:", cap(nilSlice)) // 0
fmt.Println("Is nilSlice nil?", nilSlice == nil) // true

// Добавление первого элемента
nilSlice = append(nilSlice, 1)
fmt.Println("After append 1:", nilSlice) // [1]
fmt.Println("Length:", len(nilSlice)) // 1
fmt.Println("Capacity:", cap(nilSlice)) // 1
fmt.Println("Is nilSlice nil?", nilSlice == nil) // false

// Добавление нескольких элементов
nilSlice = append(nilSlice, 2, 3, 4)
fmt.Println("After append 2, 3, 4:", nilSlice) // [1 2 3 4]
fmt.Println("Length:", len(nilSlice)) // 4
fmt.Println("Capacity:", cap(nilSlice)) // >= 4
}


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

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

🔐 База собесов | 🔐 База тестовых
🤔 Какое ключевое слово используется для объявления константы в Go?
Anonymous Quiz
96%
const
2%
constant
2%
final
0%
immutable
Что можно хранить в качестве ключа, если Float не использовать ?
Спросят с вероятностью 8%

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

1️⃣Булевый тип (bool):
        m := map[bool]string{
true: "Yes",
false: "No",
}


2️⃣Целочисленные типы (int, int8, int16, int32, int64, а также их беззнаковые эквиваленты uint, uint8, uint16, uint32, uint64):
        m := map[int]string{
1: "One",
2: "Two",
}


3️⃣Строки (string):
        m := map[string]int{
"Alice": 25,
"Bob": 30,
}


4️⃣Указатели (любого типа):
        a, b := 1, 2
m := map[*int]string{
&a: "Pointer to a",
&b: "Pointer to b",
}


5️⃣Интерфейсы (при условии, что конкретные типы, которые они содержат, поддерживают сравнение):
        var i interface{} = "string"
m := map[interface{}]string{
i: "interface as key",
}


6️⃣Составные типы (структуры, массивы), при условии, что все их поля также поддерживают сравнение с помощью оператора ==):
        type Key struct {
FirstName string
LastName string
}

m := map[Key]int{
{"Alice", "Smith": 1},
{"Bob", "Johnson": 2},
}


Почему float не рекомендуется использовать в качестве ключа

Проблемы с точностью: Плавающие числа (float) часто имеют проблемы с точностью представления. Два значения, которые математически равны, могут иметь небольшие различия в их бинарном представлении из-за ограничений точности. Это может привести к неожиданному поведению при сравнении ключей.
Стандарты IEEE 754: Плавающие числа следуют стандарту IEEE 754, который предусматривает такие значения, как NaN (Not a Number). В Go, NaN не равен сам себе (NaN != NaN), что делает их проблематичными для использования в качестве ключей в хэш-таблицах.

Рассмотрим пример использования структуры в качестве ключа в хэш-таблице:
package main

import "fmt"

type Person struct {
FirstName string
LastName string
}

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

// Вставка значений в карту
m[Person{"Alice", "Smith"}] = 25
m[Person{"Bob", "Johnson"}] = 30

// Поиск и вывод значений
fmt.Println("Alice Smith age:", m[Person{"Alice", "Smith"}])
fmt.Println("Bob Johnson age:", m[Person{"Bob", "Johnson"}])
}


В этом примере:
1️⃣Определение структуры: Создаем структуру Person с полями FirstName и LastName.
2️⃣Создание карты: Создаем карту m с ключами типа Person.
3️⃣Вставка значений: Вставляем в карту два значения с ключами типа Person.
4️⃣Поиск и вывод значений: Ищем значения по ключам и выводим их на экран.

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

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

🔐 База собесов | 🔐 База тестовых
🤔 Какой пакет используется для работы с временем в Go?
Anonymous Quiz
91%
"time"
7%
"datetime"
1%
"clock"
1%
"calendar"