JV
GO

Java to Go

10 lessons

Progress0%
1Variables & Types2Classes → Structs3Interfaces4Exception Handling → Error Values5Threads → Goroutines6Slices and Collections7Packages and Modules8Testing9Go Standard Library10Context and Cancellation
All Mirror Courses
JV
GO
Slices and Collections
MirrorLesson 6 of 10
Lesson 6

Slices and Collections

Go slices and maps vs Java List/Map — dynamic arrays and hash maps

Introduction

In this lesson, you'll learn about slices and collections in Go. Coming from Java, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
JV
From Java:

In Java, you're familiar with go slices and maps vs java list/map — dynamic arrays and hash maps.

GO
In Go:

Go has its own approach to go slices and maps vs java list/map — dynamic arrays and hash maps, which we'll explore step by step.

The Go Way

Let's see how Go handles this concept. Here's a typical example:

GO
Go Example
package main

import (
    "fmt"
    "sort"
)

func main() {
    // Slice — dynamic array (like ArrayList)
    nums := []int{1, 2, 3}
    nums = append(nums, 4)   // append returns new slice — reassign!
    fmt.Println(nums[2])     // 3
    fmt.Println(len(nums))   // 4

    // Slice expression (like subList)
    sub := nums[1:3]         // [2,3] — shares memory!
    _ = sub

    // Sort
    sort.Ints(nums)          // ascending
    sort.Sort(sort.Reverse(sort.IntSlice(nums))) // descending

    // Map (like HashMap)
    m := map[string]int{
        "alice": 30,
    }
    m["bob"] = 25

    // Safe access — two-value form
    val, ok := m["alice"]   // ok=false if missing
    if ok { fmt.Println(val) }
    // OR: default pattern
    if v, ok := m["charlie"]; !ok { v = 0; _ = v }

    delete(m, "alice")      // remove key

    // Iterate
    for k, v := range m {
        fmt.Printf("%s=%d
", k, v)
    }
}

Comparing to Java

Here's how you might have written similar code in Java:

JV
Java (What you know)
import java.util.*;

// ArrayList<Integer> — dynamic array
List<Integer> nums = new ArrayList<>(List.of(1, 2, 3));
nums.add(4);
nums.get(2);              // 3
nums.size();              // 4
nums.remove(Integer.valueOf(2)); // remove by value
nums.contains(3);         // true

// Sort
Collections.sort(nums);
nums.sort(Comparator.reverseOrder());

// HashMap<K,V>
Map<String, Integer> map = new HashMap<>();
map.put("alice", 30);
map.getOrDefault("bob", 0);   // safe get
map.containsKey("alice");     // true
map.remove("alice");

// Iterate
for (var entry : map.entrySet()) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

// ArrayList subList (like slice)
List<Integer> sub = nums.subList(1, 3); // [1,3) indices
Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

GO
In Go:

append() always returns the new slice — must reassign: s = append(s, x)

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

GO
In Go:

Slices share underlying array — modifications affect original; use copy() for independence

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

GO
In Go:

Map access returns zero value for missing key; use two-value form (v, ok) to distinguish

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

GO
In Go:

delete(m, key) removes; no equivalent to Java's remove(key) with return value

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

GO
In Go:

range over map gives (key, value) pairs — iteration order is random

Step-by-Step Breakdown

1. Append vs Add

Go's append() returns a new slice (may allocate new backing array). Java's ArrayList.add() mutates in place.

JV
Java
list.add(42);
list.add(10);
GO
Go
slice = append(slice, 42)  // MUST reassign
slice = append(slice, 10)
// Append multiple: slice = append(slice, 1, 2, 3)
Common Pitfall
Forgetting to reassign is the #1 slice bug: append(s, x) without = does nothing to s.

2. Slice Sharing

A sub-slice shares the backing array with the original. Modifying one affects the other. Use copy() to get an independent copy.

JV
Java
List<Integer> sub = new ArrayList<>(list.subList(1, 3)); // copy
GO
Go
sub := slice[1:3]           // shares backing array!
// Independent copy:
dst := make([]int, len(slice))
copy(dst, slice)

3. Map Safe Access

Accessing a missing map key returns the zero value (0 for int), not null. Use the two-value form to distinguish zero from missing.

JV
Java
map.getOrDefault("key", 0)
GO
Go
val, ok := m["key"]
if ok {
    // key exists, val is the value
} else {
    // key missing, val is zero value (0, "", etc.)
}

4. make for Pre-allocated Collections

Use make([]T, len, cap) to pre-allocate slices when size is known. Avoids repeated reallocation from append.

JV
Java
List<Integer> list = new ArrayList<>(100); // initialCapacity
GO
Go
// Pre-allocated slice
s := make([]int, 0, 100)  // len=0, cap=100
for i := 0; i < 100; i++ { s = append(s, i) }
// Pre-filled map
m := make(map[string]int, 100)

Common Mistakes

When coming from Java, developers often make these mistakes:

  • append() always returns the new slice — must reassign: s = append(s, x)
  • Slices share underlying array — modifications affect original; use copy() for independence
  • Map access returns zero value for missing key; use two-value form (v, ok) to distinguish
Common Pitfall
Don't assume Go works exactly like Java. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • append returns new slice — always reassign: s = append(s, item)
  • Sub-slices share memory; copy(dst, src) for independent copies
  • Map: two-value form (v, ok) := m[k] distinguishes missing from zero value
  • make([]T, len, cap) pre-allocates for performance when size is known
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Java code in Go to practice these concepts.
PreviousNext