Разбираем: array, slice, map, zero value - в Go vs Java | Types - Language

Серия: Go для Java-разработчиков

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

Зачем вообще разбирать базовые структуры

В Java почти все коллекции — это классы из стандартной библиотеки. Вы создаёте объект ArrayList, HashMap, HashSet и работаете с ними.

В Go всё устроено немного иначе. Многие структуры данных встроены прямо в язык.

  • array — фиксированный массив
  • slice — динамическая структура поверх массива
  • map — хеш-таблица
  • zero value — встроенное значение по умолчанию

Именно поэтому важно понять, как они связаны с памятью. Go делает многие вещи проще, но иногда — менее очевидно для Java-разработчика.


Array в Go

Array в Go — это фиксированный массив. Размер является частью типа.

Это значит, что [3]int и [5]int — разные типы.

Пример

// объявляем массив из 3 элементов
var nums [3]int

// записываем значения
nums[0] = 10
nums[1] = 20
nums[2] = 30

// вывод
fmt.Println(nums)

Java эквивалент:

// массив фиксированного размера
int[] nums = new int[3];

nums[0] = 10;
nums[1] = 20;
nums[2] = 30;

System.out.println(Arrays.toString(nums));

На первый взгляд — всё одинаково. Но в Go массивы используются значительно реже.

В реальном коде Go почти всегда используют slice, а не array. Array чаще применяется как внутренний механизм хранения данных.

Slice — основной контейнер Go

Slice — это динамическая структура, которая работает поверх массива.

Фактически slice — это небольшой дескриптор, который содержит три вещи:

  • указатель на массив
  • длину
  • capacity (ёмкость)

Пример slice

// создаем slice
var nums []int

// добавляем элемент
nums = append(nums, 1)

// добавляем ещё
nums = append(nums, 2, 3)

fmt.Println(nums)

Java эквивалент:

// динамический список
List<Integer> nums = new ArrayList<>();

// добавляем элементы
nums.add(1);
nums.add(2);
nums.add(3);

System.out.println(nums);

Поведение похоже на ArrayList, но реализовано иначе.


Slice Backing Array

Самая важная концепция — backing array.

Slice хранит ссылку на массив, в котором реально лежат данные.

Схематически это выглядит так:

Slice
 ├─ pointer ────────────┐
 ├─ length              │
 └─ capacity            │
                        ▼
Backing Array
[10][20][30][40][50]

Если сделать новый slice, он может указывать на тот же массив.

Пример

// исходный массив
arr := [5]int{1,2,3,4,5}

// создаем slice
s1 := arr[1:4]

fmt.Println(s1)

Java аналог (условный)


// в Java подобного механизма нет напрямую

int[] arr = {1,2,3,4,5};

// обычно копируют часть массива
int[] slice = Arrays.copyOfRange(arr, 1, 4);

System.out.println(Arrays.toString(slice));

В Java чаще происходит копирование, а в Go — совместное использование памяти.

Несколько slice могут ссылаться на один backing array. Изменение данных через один slice изменит данные во всех.

Что происходит при append

Если capacity позволяет, новые элементы добавляются в тот же массив.

Если места не хватает — Go создаёт новый массив и копирует данные.

append()

Slice
  ↓
capacity хватает?
   │
   ├─ да → запись в тот же массив
   │
   └─ нет → новый массив + копирование
Это похоже на внутреннюю работу ArrayList, который также увеличивает внутренний массив.

Map в Go

Map — это встроенная хеш-таблица.

Она похожа на HashMap из Java, но является частью языка.

Пример

// создаем map
ages := make(map[string]int)

// добавляем значения
ages["Alice"] = 30
ages["Bob"] = 25

fmt.Println(ages["Alice"])

Java эквивалент:


// HashMap
Map<String, Integer> ages = new HashMap<>();

ages.put("Alice", 30);
ages.put("Bob", 25);

System.out.println(ages.get("Alice"));

Zero Value — важная особенность Go

В Go каждая переменная автоматически получает значение по умолчанию.

Это называется zero value.

Тип Zero value Комментарий
int 0 числовой ноль
string "" пустая строка
bool false логическое значение
slice nil но append всё равно работает
map nil нужно создать через make

Пример slice без инициализации


// nil slice
var nums []int

// append работает
nums = append(nums, 1)

fmt.Println(nums)

Java аналог


// в Java переменная будет null
List<Integer> nums = null;

// nums.add(1) вызовет NullPointerException
Zero value позволяет писать меньше защитного кода. Многие структуры можно использовать сразу после объявления.

Сравнение Go и Java

Концепция Go Java Комментарий
Массив array array похожи
Динамический список slice ArrayList slice встроен в язык
Хеш-таблица map HashMap map часть языка
Значение по умолчанию zero value null / 0 в Go меньше ошибок null
Управление памятью slice + backing array внутренний массив похоже на ArrayList

Constants / Константы

В Go константы объявляются через const и хранят неизменяемые значения. Можно создавать одиночные или групповые константы. Отличие от Java — типы могут быть неявными и поддерживается iota для автоматической генерации последовательностей.

Пример одиночной константы

// Go
const Pi = 3.14159

func main() {
    fmt.Println(Pi)
}

Java эквивалент:

// Java
class Main {
    static final double PI = 3.14159;

    public static void main(String[] args) {
        System.out.println(PI);
    }
}

Пример групповых констант с iota

// Go
const (
    Sunday = iota
    Monday
    Tuesday
)

func main() {
    fmt.Println(Sunday, Monday, Tuesday) // 0 1 2
}

Java эквивалент через enum:

// Java
enum Day { SUNDAY, MONDAY, TUESDAY }

public class Main {
    public static void main(String[] args) {
        System.out.println(Day.SUNDAY.ordinal()); // 0
        System.out.println(Day.MONDAY.ordinal()); // 1
        System.out.println(Day.TUESDAY.ordinal()); // 2
    }
}
Совет: используйте iota для последовательных числовых констант. Enum в Java обеспечивает похожую функциональность, но более строго типизирован.

Практический итог

Для Java-разработчика важнее всего понять одну вещь:

в Go контейнеры — это не столько классы, сколько языковые конструкции.

Главные отличия:

  • slice заменяет большинство случаев использования ArrayList
  • map — встроенная хеш-таблица
  • zero value снижает количество ошибок
  • несколько slice могут разделять одну память
Самая частая ошибка начинающих Go-разработчиков — забывать, что slice может разделять backing array. Это может приводить к неожиданным изменениям данных.

Практическое правило:

  • используйте slice вместо array
  • помните про append и capacity
  • используйте map для ассоциативных структур
  • помните, что zero value часто уже готов к использованию

Когда эти принципы становятся привычными, код на Go начинает ощущаться значительно проще, чем код на Java с большим количеством объектов-обёрток.


Всего лайков:0
Мой канал в социальных сетях
Отправляя email, вы принимаете условия политики конфиденциальности

Полезные статьи:

Как удержать легаси-проект от смерти и подарить ему ещё 10 лет
Признаки легаси-проекта: как распознать старый корабль Легаси — это не просто старый код. Это живой организм, который пережил десятки изменений, смену команд, устаревшие технологии и множество временн...
Типы данных в Java
Типы данных в Java Привет! С вами Виталий Лесных. В этом уроке курса «Основы Java для начинающих» разберем, что такое типы данных. Типы данных — это фундамент любого языка программирования. С их помо...
Современные архитектурные подходы: от монолита к событийным системам
Введение Архитектура — это не просто способ расположить классы и модули. Это язык, на котором система разговаривает со временем. Сегодня Java-разработчик живёт в мире, где границы между сервисами, по...

Новые статьи:

Resource cleanup, rate‑limiting strategies, bounded vs unbounded channels - в Go vs Java | Паттерны, идиомы и лучшие практики Go
Продолжаем серию статей для разработчиков, которые хотят изучить Go на основе знаний Java, и наоборот. В этой статье мы обсудим три ключевые темы: Resource Cleanup (освобождение ресурсов), Rate-Limiti...
Разбираем: Rate‑limiter, non‑blocking operations, scheduler  Go vs Java | Concurrency часть 4
Эта статья посвящена пониманию принципов работы с конкурентностью и синхронизацией в Go и Java. Мы рассмотрим ключевые подходы, такие как rate‑limiter, неблокирующие операции и планирование задач, сра...
Разбираем: Trace, Profiling, Integration Testing, Code Coverage, Mocking, Deadlock Detection в Go vs Java | Testing, Debugging и Profiling
Серия: Go для Java-разработчиков — разбор trace, профилирования и тестирования В этой статье мы разберем инструменты и практики для тестирования, отладки и профилирования в Go. Для Java-разработчика ...
Fullscreen image