Slice
Una slice è un involucro intorno ad un array. Possiede:
reference
. riferimento all’elemento iniziale dell’array- default: primo elemento
length
- dimensione della slice- default: fino alla fine dell’array
capacity
- capacità o spazio preallocato per la slice>= length
b := a[2, 5]
Da 2 incluso fino a 5 escluso.
Mentre un array ha allocazione statica, una slice ha allocazione dinamica, cioè nello heap.
Le slice vengono usate, per esempio, come segue:
(090slices.go):
package main
import "fmt"
func main() {
// Creazione di slice di tre elementi iniziali
// Allocata in memoria dinamica - heap
// Gli elementi sono zero di default
s := make([]string, 3)
fmt.Println("emp:", s)
// Uso simile ad array
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])
// Funzione di lunghezza della slice
fmt.Println("len:", len(s))
// Estensione di una slice
// Può silenziosamente riallocare memoria
// pertanto è necessaria un'assegnazione alla
// nuova locazione in memoria (s è un puntatore implicito)
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println("apd:", s)
// Semplice assegnazione copia solo il puntatore
// poichè sono nello heap la copia è per riferimento
p := s
// Cambiare una slice cambia anche l'altra
p[1] = "x"
fmt.Println("old: ", s)
fmt.Println("new: ", p)
// Copia di una slice
// Crea nuova slice
c := make([]string, len(s))
// copia su destinazione di sorgente
copy(c, s)
// le due slice sono ora differenti
c[1] = "y"
fmt.Println("old: ", s)
fmt.Println("copy: ", c)
// Operatore di slice - fetta
// Dal primo indicato incluso all'ultimo indicato escluso
l := s[2:5]
fmt.Println("sl1:", l)
// Dal primo assoluto incluso all'ultimo indicato escluso
l = s[:5]
fmt.Println("sl2:", l)
// Dal primo indicato incluso all'ultimo assoluto
l = s[2:]
fmt.Println("sl3:", l)
// Dichiarazione e inizializzazione simultanea di slice
// Se tra quadre non c'è dimensione è una slice
// se c'è è un array
t := []string{"g", "h", "i"}
fmt.Println("dcl:", t)
// Creazione di una slice vuota
// Le graffe sono obbligatorie
z := []string{}
// Una variabile dichiarata deve sempre essere usata
fmt.Println(">>", z, "<<")
// Slice multidimensionale
// Entrambe le dimensioni sono dinamiche:
// puntatori a puntatori
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
Una slice deve essere allocata in memoria dinamica, con l'operatore make
, simile al new
di C++ e Java:
s := make([]string, 3)
Occorre indicare il tipo degli elementi della slice e la dimensione iniziale dell'allocazione.
Una slice inizialmente è vuota, contiene elementi azzerati.
Si possono settare e accedere a elementi della slice iniziale come per un array,e la funzione len()
conosce la dimensione della slice.
E' possibile estendere la slice iniziale con la funzione append()
che prende almeno due argomenti: il nome della slice e gli elementi aggiunti alla fine.
Si può copiare una slice in un'altra slice che abbia almeno la dimensione della prima.
Si chiama slice perchè è possibile estrarre una fetta della slice originale in un'altra variabile, dichiarata e inizializzata dello stesso tipo. Nell'esempio:
a := s[2:5]
a
contiene gli elementi di s a partire dall'elemento di indice 2 (il terzo) incluso e a terminare all'elemento di indice 5 (il sesto) escluso.
Se l'inizio della fetta - prima dei due punti - non è indicato, si intende il primo elemento incluso. Se la fine della fetta - dopo i due punti - non è indicato, si intende l'ultimo elemento incluso.
Si può avere una slice di slices. Per esempio con la dichiarazione:
twoD := make([][]int, 3)
si crea una slice di tre slices.
La dimensione esterna è di tre elementi (slices). la dimensione di ogni elemento interno (slice) non è specificata, e possono essere slice interne di dimensione differente.
Ogni slice interna deve a sua volta essere costruita con, p.es:
twoD[i] = make([]int, innerLen)
Si nota, per chi proviene dal C o C++, la presenza nascosta di puntatori a memoria allocata dinamicamente.
Esempio 1
(091slice1.go):
package main
import "fmt"
func main() {
a := [...]int{0, 10, 20, 30, 40, 50, 60, 70} // array
fmt.Println(len(a)) // 8
fmt.Println(a)
b := a[2:6] // da 2 a 6 escluso
fmt.Println(len(b), cap(b)) // 4 6
fmt.Println(b)
fmt.Println(b[1]) // indicizza una slice, non un array
c := a[5:] // da 5 alla fine
fmt.Println(c)
d := a[:5] // dall’inizio a 5 escluso
fmt.Println(d)
e := a[:] // la slice copre tutto a
fmt.Println(e)
f := a // array, non slice
fmt.Println(f)
g := d[:3] // slice di slice
fmt.Println(g)
h := []int{1, 2, 3, 4} // slice con array sottostante
fmt.Println(h)
fmt.Println(len(h), cap(h)) // 4 4
}
Esempio 2
append
aggiunge elementi alla fine della slice:
- Length e capacity sono estese
- Capacity può essere maggiore di length
(092slice.app.go):
package main
import "fmt"
func main() {
sl1 := []string{"alpha", "bravo", "charlie"}
fmt.Println("old slice : ", sl1)
fmt.Println("old length : ", len(sl1))
fmt.Println("old capacity : ", cap(sl1))
sl1 = append(sl1, "delta")
fmt.Println("new slice : ", sl1)
fmt.Println("new length : ", len(sl1))
fmt.Println("new capacity : ", cap(sl1))
}
Esempio 3
Svuotare una slice. Due modi:
[092slice-del.go):
package main
import "fmt"
func main() {
a := []string{"A", "B", "C", "D", "E"}
fmt.Println(a, len(a), cap(a)) // [A B C D E] 5 5
a = nil // tootalmente ripulita
fmt.Println(a, len(a), cap(a)) // [] 0 0
b := []string{"A", "B", "C", "D", "E"}
b = b[:0] // slice con length zero, ma ha ancora capacity
fmt.Println(b, len(b), cap(b)) // [] 0 5
}
Esempio 4
Rimuovere un elemento. La slice deve essere ricostruita.
(094slice-rem.go):
package main
import "fmt"
func main() {
strSlice := []string{"Canada", "Japan", "Germany", "Italy"}
fmt.Println(strSlice)
// append di più elementi
strSlice = append(strSlice, "UK", "France", "Spain")
fmt.Println(strSlice)
// rimuove l’elemento con indice 2
strSlice = append(strSlice[:2], strSlice[3:]...)
fmt.Println(strSlice)
// rimuove il primo elemento
strSlice = strSlice[1:]
fmt.Println(strSlice)
// rimuove l’ultimo elemento
strSlice = strSlice[:len(strSlice)-1]
fmt.Println(strSlice)
}