6. GO array (array), slice (slice), make et new opérations, map (dictionnaire)

1.tableau (tableau)

Les tableaux ont une longueur déterminée. La mémoire utilisée a été déterminée. Lorsque la longueur du tableau n'est pas suffisante, elle ne s'étendra pas dynamiquement et ne demandera pas automatiquement une nouvelle mémoire.

Déclaration et format du tableau :

//一维数组
var a = [5]int{12, 34, 56, 78, 90}
//也可以这样写
b := [5]int{21, 43, 65}
fmt.Println("a的长度和容量:", len(a), cap(a))
fmt.Println("b的长度和容量:", len(b), cap(b))
fmt.Println("a数组为:", a, "b数组为:", b)

//二维数组
var c = [4][5]int{
   
   {1, 2, 3}, {4, 5, 6}}
//也可以这样写
d := [4][5]int{[5]int{1, 2, 3}, [5]int{4, 5, 6}}
fmt.Println("d的行的长度和容量:", len(d), cap(d))
fmt.Println("d的某行中列的长度和容量", len(d[1]), cap(d[1]))
fmt.Println("c数组为:", c, "d数组为:", d)

output
a的长度和容量: 5 5
b的长度和容量: 5 5
a数组为: [12 34 56 78 90] b数组为: [21 43 65 0 0]
d的行的长度和容量: 4 4
d的某行中列的长度和容量 5 5
c数组为: [[1 2 3 0 0] [4 5 6 0 0] [0 0 0 0 0] [0 0 0 0 0]] d数组为: [[1 2 3 0 0] [4 5 6 0 0] [0 0 0 0 0] [0 0 0 0 0]]

Nous pouvons clairement voir que la longueur et la capacité du réseau sont cohérentes. La caractéristique des tableaux est qu'ils sont déclarés comme ayant une longueur fixe et ne peuvent pas être étendus sur place. Par conséquent, dans Golang, les tranches sont généralement utilisées à la place lorsque des tableaux sont nécessaires.

2. tranche

La déclaration d'une tranche est la même que la déclaration d'un tableau, sauf que les tranches peuvent être développées dynamiquement.

déclaration de tranche :

var a []int
//也可以
a:=[]int{1,2,3}

L'essence du découpage est de référencer le pointeur de tête du tableau sous-jacent + la longueur actuelle de la tranche + la taille du tableau sous-jacent : array, len et cap :

La structure de la tranche :

type slice struct {
   array unsafe.Pointer    // 底层数组头指针
   len   int               // 当前切片长度 
   cap   int               // 底层数组最大容量
}

Exemple:

Nous définissons u comme tableau sous-jacent et s1 comme première tranche.

	u := [10]byte{11, 12, 13, 14, 15, 16, 17, 18, 19, 20} // 长度为10的数组
	s1 := u[3:7]                                          // 从数组的第3个元素至第6个元素建立切片(前闭后开区间)
	fmt.Println(len(s1), cap(s1))
	fmt.Println("u=", u, "s1=", s1)

output
4 7
u= [11 12 13 14 15 16 17 18 19 20] s1= [14 15 16 17]

Insérer la description de l'image ici

 2. Nous utilisons ensuite le tableau sous-jacent pour le découpage, qui est s2.

Insérer la description de l'image ici

	//修改s1的值
    s1[0] = 45
	s1[3] = 89
	s2 := u[2:8]
	fmt.Println(len(s2), cap(s2))
	fmt.Println("u=", u, "s1=", s1, "s2=", s2)

output
6 8
u= [11 12 13 45 15 16 89 18 19 20] s1= [45 15 16 89] s2= [13 45 15 16 89 18]

 Grâce aux résultats de sortie, nous pouvons constater que deux tranches s'influencent mutuellement lorsqu'elles agissent sur un tableau sous-jacent.

Mécanisme d'expansion :

Avez-vous des doutes après avoir vu cela ? Puisque la taille des données a été spécifiée lors de l'initialisation, pourquoi devez-vous spécifier la taille réservée ? En effet, make() utilise un algorithme de tableau dynamique. Il s'applique d'abord au système d'exploitation pour un petit morceau de mémoire. C'est le plafond. Une fois que le plafond est entièrement occupé par len, il doit être étendu. L'expansion est un tableau dynamique, puis va au système d'exploitation. Allouez de la mémoire deux fois la longueur actuelle et copiez les anciennes données dans le nouvel espace mémoire.

Utilisez le code comme exemple :

var data []int
for i, n := 0, 20; i < n; i++ {
	data = append(data, 1)
	fmt.Printf("len=%d cap=%d\n", len(data), cap(data))
}

//output
len=1 cap=1		# 第一次扩容
len=2 cap=2		# 第二次扩容
len=3 cap=4		# 第三次扩容
len=4 cap=4
len=5 cap=8		# 第四次扩容
len=6 cap=8
len=7 cap=8
len=8 cap=8
len=9 cap=16	# 第五次扩容
len=10 cap=16
len=11 cap=16
len=12 cap=16
len=13 cap=16
len=14 cap=16
len=15 cap=16
len=16 cap=16
len=17 cap=32	# 第六次扩容
len=18 cap=32
len=19 cap=32
len=20 cap=32

D'après les résultats ci-dessus, nous pouvons constater que lorsque la capacité est supérieure à la capacité maximale que nous avons fixée, elle sera augmentée de deux fois la capacité maximale actuelle, mais lorsqu'elle sera étendue dans une certaine mesure, elle sera inférieure à 2 fois l'expansion.

fonctions intégrées de tranche

  • cap() : la capacité maximale de la tranche. Les valeurs cap() de Slice_a et Slice_b dans l'exemple ci-dessus sont respectivement 8 et 5. Formellement, puisque Slice_a a une capacité de 8, l'affectation à Slice_b est valide.
  • len() : La longueur de la tranche, qui a été introduite dans l'exemple ci-dessus. Les valeurs len() de Slice_a et Slice_b sont respectivement 3 et 2.
  • append() : ajoute un ou plusieurs éléments à la tranche, puis renvoie une tranche du même type que la tranche
  • copy() : La fonction copy copie les éléments du src de la tranche source vers le dst cible et renvoie le nombre d'éléments copiés.

3. création et nouvelles opérations

make et new peuvent être utilisés pour l’allocation de mémoire.

  • new est utilisé pour différents types d'allocation de mémoire. C'est essentiellement la même fonction que new de Java. new(T) renvoie un pointeur vers une valeur zéro nouvellement allouée de type T.
  • make ne peut être utilisé que pour allouer de la mémoire aux types slice, map et canal, et renvoie un type T avec une valeur initiale (différente de zéro), pas un pointeur T.

La déclaration make est :

make(Type, len, cap)

Il comporte trois paramètres :

  • Type : type de données, paramètres obligatoires. La valeur de Type ne peut être que de trois types de données : tranche, carte et canal.
  • len : La longueur de l'espace mémoire réellement occupé par le type de données. Map et Channel sont des paramètres facultatifs, et slice est un paramètre obligatoire.
  • cap : La longueur de l'espace mémoire réservé à l'avance pour le type de données, paramètre facultatif. La soi-disant réservation anticipée signifie demander de l'espace mémoire supplémentaire à l'avance lors de la demande d'espace mémoire pour le type de données.Cela peut éviter la surcharge causée par l'allocation secondaire de mémoire et améliorer considérablement les performances du programme.

 Il y a des pertes dans l'extension de capacité, donc déclarez le plafond de la tranche lorsque la longueur maximale des données est connue autant que possible pour éviter la perte de performances causée par les changements fréquents de capacité de bibliothèque de la baie sous-jacente :

//没有设置cap
func BenchmarkAppendZeroSlice(b *testing.B) {
	maxNum := 10000
	for i := 0; i < b.N; i++ {
		// 空切片
		var zeroSlice []int
		for j := 0; j < maxNum; j++ {
			zeroSlice = append(zeroSlice, j)
		}
	}
}

//设置了cap
func BenchmarkAppendCapSlice(b *testing.B) {
	maxNum := 10000
	for i := 0; i < b.N; i++ {
		// 声明cap的切片
		capSlice := make([]int, 0, maxNum)
		for j := 0; j < maxNum; j++ {
			capSlice = append(capSlice, j)
		}
	}
}

Sa vitesse de test est la suivante :

PS D:\Project\GoLang\test> go test -bench .
goos: windows
goarch: amd64
pkg: test
cpu: AMD Ryzen 5 5600U with Radeon Graphics
BenchmarkAppendZeroSlice-12        28065             41915 ns/op
BenchmarkAppendCapSlice-12        108114             11399 ns/op
  • BenchmarkAppendZeroSlice-12 signifie que le nombre de cœurs de processeur utilisés par cette fonction est de 12.
  • 28065 est le nombre d'exécutions
  • 41915 ns/op est le temps moyen par heure

D'après les résultats, nous pouvons voir que si le plafond est attribué à l'avance, le temps d'exécution est de 11 399 ns, mais s'il n'est pas défini à l'avance, il est de 41 915 ns. Il y a un grand écart de performances. Nous devrions donc faire de notre mieux pour fixer le plafond en premier.

4. carte

 instruction cartographique :

a := make(map[string][]int)
a["tom"] = append(a["tom"], 56, 78, 90)
a["jack"] = append(a["jack"], 67, 77, 34)
fmt.Println(a)


output
map[jack:[67 77 34] tom:[56 78 90]]

Lien de référence :

Analyse du principe Golang : principe de tranche et mécanisme d'expansion_go langage slice expansionprincipe_pbrong's blog-CSDN blog

 Bases du langage Go : opérations sur les tableaux, les tranches, les make et les nouvelles, tableau du langage map_go := blog de make_Youyoudou-blog CSDN

Fonction intégrée de Golang : make()_golang make_GeekDianer's Blog-CSDN Blog 

Guess you like

Origin blog.csdn.net/qq_48480384/article/details/129944976