gopherpath
0%

Slices are headers, maps are pointers

40 min · lesson 1 of 3 · #slices · #maps

A Go slice is not a list. It is a three-word header: {pointer, len, cap}. Copying a slice copies the header, not the elements — which is exactly the ArrayList aliasing you already know, plus a cap nobody told you about.

Go
package main

import "fmt"

func main() {
	xs := make([]int, 3, 10) // len 3, cap 10
	ys := xs                 // header copy — same backing array
	ys[0] = 42
	fmt.Println(xs[0]) // 42: aliasing, same as Java

	fmt.Println(len(xs), len(ys), cap(xs))
	fmt.Println(&xs[0] == &ys[0]) // true — identical storage
}
Java
var xs = new ArrayList<>(List.of(0, 0, 0));
var ys = xs;             // same object, same aliasing
ys.set(0, 42);           // xs.get(0) == 42
int[] arr = new int[3];  // fixed size: no capacity concept, no growth
TypeScript
const xs = [1, 2, 3];
const ys = xs;      // same array reference
ys[0] = 42;         // xs[0] === 42
ys.push(4);         // length grows in place — no separate cap

append returns a new header

append writes into spare capacity if it can, and allocates a copy when it cannot. Both outcomes are legal, which is why the result must be assigned:

Go
package main

import "fmt"

func main() {
	base := make([]int, 3, 10) // len 3, cap 10
	a := append(base, 1)       // fits → same array
	b := append(base, 2)       // fits → same array again
	fmt.Println(a[3], b[3])    // 2 2 — a[3] was overwritten

	full := make([]int, 3) // len == cap
	c := append(full, 1)   // no spare capacity → new array
	d := append(full, 2)   // new array again
	fmt.Println(c[3], d[3]) // 1 2 — independent
}
Java
List<Integer> base = new ArrayList<>(List.of(0, 0, 0)); // capacity managed for you
base.add(1);   // never aliases silently; growth is invisible
List<Integer> copy = List.copyOf(base);                 // explicit copy
TypeScript
const base = [0, 0, 0];
const a = [...base];   // explicit spread = real copy

Trap: passing a subslice to a goroutine and appending to it inside that goroutine is a data race on the shared array. xs[:len(xs):len(xs)] (three-index slice) caps it so append is forced to copy.

Maps: reference type, no order, no concurrency

Go
package main

import "fmt"

type Account struct{ Balance int }

func main() {
	m := map[string]*Account{"a": {Balance: 10}}
	alias := m // same hash table, like Java
	alias["b"] = &Account{Balance: 99}
	fmt.Println(len(m)) // 2

	v, ok := m["missing"] // comma-ok, the Go Optional
	fmt.Println(v, ok)

	m["a"].Balance += 5 // fine: pointer value
	fmt.Println(m["a"].Balance)

	delete(m, "b")
	fmt.Println(len(m))
}
Java
Map<String, Account> m = new HashMap<>();
m.put("a", new Account(10));
Account v = m.get("missing");     // null — no comma-ok, use getOrDefault/containsKey
m.get("a").balance += 5;          // objects are references
TypeScript
const m = new Map<string, Account>([["a", { balance: 10 }]]);
const v = m.get("missing");   // undefined
m.get("a")!.balance += 5;     // optional chaining or a non-null assertion

Three facts that bite Java developers:

  1. Iteration order is randomised on purpose. No LinkedHashMap by default; sort keys yourself.
  2. Concurrent writes are a fatal runtime error, not a caught exception — you cannot recover from it. Use sync.Mutex, sync.Map (for read-mostly caches), or a channel.
  3. A struct value is not addressable inside the map: m["a"].Balance++ fails to compile for map[string]Account. Store pointers, or read-modify-write.

What replaces which

Java Go
ArrayList<T> []T (with explicit cap)
HashMap<K,V> map[K]V
ConcurrentHashMap sync.Map or map + sync.RWMutex
Collections.unmodifiableList copy the slice (there is no read-only slice)
Arrays.asList(...) []T{...}
stream filter/map a for loop (the honest answer)

Proof checkpoint

  • Make a slice s := make([]int, 0, 4), append 4 items, then t := append(s, 5) and prove whether t shares storage with s (hint: &t[0] == &s[0]).
  • Write a map read that distinguishes "absent" from "present with zero value".
  • Explain in one sentence why cap exists at all (spoiler: allocation control, not safety).
stored in this browser only