go 集合数据结构

数组

package main
import (
	"fmt"
)
func main(){
	//数组
	var s1 [3]string=[3]string{"t","你好","小杜"}//数组初始化
	var s2 [4]string
	s2[0]="你好"
	s1[0]="m"
	for i,v:=range s2{
		fmt.Println(i,v)
	}
	//数组初始化
	var R1 = [...]string{"wc","你好","小鹿"}
	for _,v:=range R1{
		fmt.Println(v)
	}
	R2:= [...]string{"wc","你好","小鹿"}
	for _,v:=range R2{
		fmt.Println(v)
	}
	R3:= [3]string{2:"小熊"}
	for _,v:=range R3{
		fmt.Println(v)
	}

}
执行结果
PS D:\golang\goproject\src\lnhgo> go run day02\demo01\main.go
0 你好
1 
2 
3 
wc
你好
小鹿
wc
你好
小鹿


小熊

  数组比较

package main
import (
	"fmt"
)
func main(){
	//数组初始化
	var R1 = [...]string{"wc","你好","小鹿"}
	for _,v:=range R1{
		fmt.Println(v)
	}
	R2:= [...]string{"wc","你好","小鹿"}
	for _,v:=range R2{
		fmt.Println(v)
	}
	if R1==R2{
		fmt.Println("exit")
	}
	

}
执行结果
wc
你好
小鹿
wc
你好
小鹿
exit

  多维数组

package main
import (
	"fmt"
)
func main(){
	//初始化
	var str [3][4]string= [3][4]string{{"go","1h","bobby","go体系课程"},{"python","2h","橘子","python体系课程"},{"grpc","2H","小鹿","grpc"}}
	//遍历
	for _,v:=range str{
		for _,a:=range v{
			fmt.Println(a)
		}
	}
	

}

  切片介绍

 

这是 Go 切片最重要的知识点之一:切片赋值不会复制数据,只会复制切片描述符(header)

你的代码:

s2 = s1
s3 = s1[:]
s5 := s1

这三种写法都没有复制底层数组。


切片内部结构

切片本质上只有三个字段:

type slice struct {
    ptr *T
    len int
    cap int
}

例如:

s1 := []string{"go","mysql","es"}

内存大概是:

s1
 ┌─────────┐
 │ ptr ────┼─────┐
 │ len=3   │     │
 │ cap=3   │     │
 └─────────┘     │
                 ▼

        ┌────────┬────────┬────────┐
        │  go    │ mysql  │  es    │
        └────────┴────────┴────────┘

s2 = s1

s2 = s1

只是把 header 复制一份:

s1.ptr ─┐
        ▼
      底层数组

s2.ptr ─┘

两个切片指向同一个数组。

所以:

s1[0] = "python"

实际上改的是:

底层数组[0] = "python"

因此:

fmt.Println(s2)

也会看到:

[python mysql es]

s3 = s1[:]

很多人以为这是复制。

其实不是。

s3 = s1[:]

只是重新切片:

s3 = s1[0:len(s1)]

仍然指向同一个数组。

s1.ptr ─┐
        ▼
      底层数组

s3.ptr ─┘

因此也会受影响。


s5 := s1

s2 = s1

完全一样。

只是短变量声明。


copy()

你这里:

var s4 []string = make([]string, 3)

copy(s4, s1)

第一步:

make([]string,3)

创建了新的底层数组:

s4
 │
 ▼

┌────┬────┬────┐
│    │    │    │
└────┴────┴────┘

然后:

copy(s4,s1)

把元素逐个复制过去:

s1                         s4

┌────┬───────┬────┐      ┌────┬───────┬────┐
│ go │ mysql │ es │ ──► │ go │ mysql │ es │
└────┴───────┴────┘      └────┴───────┴────┘

此时两个数组已经独立。

所以:

s1[0] = "python"

变成:

s1:
[python mysql es]

s4:
[go mysql es]

怎么验证是不是同一个底层数组

打印地址:

fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", s2)
fmt.Printf("%p\n", s3)
fmt.Printf("%p\n", s4)
fmt.Printf("%p\n", s5)

你会看到类似:

0xc0000100c0
0xc0000100c0
0xc0000100c0
0xc000012120
0xc0000100c0

说明:

s1 s2 s3 s5
      │
      ▼
同一个底层数组

s4
 │
 ▼
独立底层数组

记住一句话:

切片赋值(=)复制的是切片头,不复制数据;copy() 才会复制底层数据。

这也是为什么 Go 面试里经常问:

a := []int{1,2,3}
b := a

b[0] = 100

fmt.Println(a)

答案是:

[100 2 3]

因为 ab 指向同一个底层数组。

 

package main
import(
	"fmt"
)
func main(){
	var c []string
	c=append(c, "t")
	fmt.Printf("%T\n" ,c)	
	//切片初始化:1从数组直接创建2.使用make,3直接声明{}
	var a1=[4]string{"小狗","小鸡","小鸭","小鹿"}
	//使用make创建
	var b1 = make([]string,5)
	copy(b1,a1[0:2])//左闭右开
	C1:=a1[0:1]
	fmt.Printf("%T\n",C1)
	var b2 []string
	//b2[0]="t"会报错因为没有初始化空间
	b2 = append(b2, "R")//调用函数不会报错
	var b3 []string=make([]string, 6)
	copy(b3,a1[:])
	fmt.Println(b3[:3])//左闭右开
	//合并两个切片
	s1:=[]string{"go","grpc"}
	s2:=[]string{"mysql","es"}
	s1=append(s1, s2...)
	
	s3:=[]string{"kafka","nginx","apache"}
	s1= append(s1, s3[1:]...)
	fmt.Println(s1)

    //删除中间某个元素
	s1=append(s1[:2],s1[3:]...)
	fmt.Println(s1)
	//删除开头某个元素
	s1=s1[1:]
	fmt.Println(s1)
	//删除结尾元素
	s1=s1[:len(s1)-1]
	fmt.Println(s1)
	//复制切片
	s2=s1//拷贝底层数组地址值
	s3=s1[:]//拷贝底层数组地址值
	var s4 []string = make([]string, 3)//拷贝要有空间
	copy(s4,s1)//拷贝底层数组的元素值
	s5:=s1//拷贝底层数组地址值

	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)
	fmt.Println(s4)
	fmt.Println(s5)
	fmt.Println("===============================")
	s1[0]="python"
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)
	fmt.Println(s4)
	fmt.Println(s5)
	fmt.Println("+++++++++++++++++++++++++++++++++++++")
	fmt.Printf("s1地址值=%p\n",&s1)
	fmt.Printf("s2地址值=%p\n",&s2)
	fmt.Printf("s3地址值=%p\n",&s3)
	fmt.Printf("s4地址值=%p\n",&s4)
	fmt.Printf("s5地址值=%p\n",&s5)
	fmt.Println("+++++++++++++++++++++++++++++++++++++")
	fmt.Printf("s1存储的底层数组地址值=%p\n",s1)
	fmt.Printf("s2存储的底层数组地址值=%p\n",s2)
	fmt.Printf("s3存储的底层数组地址值=%p\n",s3)
	fmt.Printf("s4存储的底层数组地址值=%p\n",s4)
	fmt.Printf("s5存储的底层数组地址值=%p\n",s5)
	//fmt.Printf("s6地址址值=%p\n",&s1)
}


执行结果
[]string
[]string
[小狗 小鸡 小鸭]
[go grpc mysql es nginx apache]
[go grpc es nginx apache]
[grpc es nginx apache]
[grpc es nginx]
[grpc es nginx]
[grpc es nginx]
[grpc es nginx]
[grpc es nginx]
[grpc es nginx]
===============================
[python es nginx]
[python es nginx]
[python es nginx]
[grpc es nginx]
[python es nginx]
+++++++++++++++++++++++++++++++++++++
s1地址值=0xc000094078
s2地址值=0xc000094090
s3地址值=0xc0000940a8
s4地址值=0xc000094120
s5地址值=0xc000094138
+++++++++++++++++++++++++++++++++++++
s1存储的地址值=0xc0000b6010
s2存储的地址值=0xc0000b6010
s3存储的地址值=0xc0000b6010
s4存储的地址值=0xc0000880f0
s5存储的地址值=0xc0000b6010

  说明go

package main
import (
	"fmt"
)
func main(){
	data:=[]int{1,2,3,4,5,6,7,8,9,10}
	s1:=data[1:6]
	s2:=data[2:7]
	s2=append(s2, 1,2,8,89,14,17,18)//超出容量会拷贝扩容
	s2[0]=71//这时候改的是新独立出来的底层数组元素地址;所以跟s1不一样
	fmt.Println("s1=",s1)
	fmt.Println("s2=",s2)
	//s2=append(s2, 9,10,11,23,22,35,78,79,90,81,67,43,32,45,43,57,75,98,67,43,15,16,18,16,87,23,76,48,35,24,88,77)
	// fmt.Println("s1=",s1)
	// fmt.Println("s2=",s2)
}
结果
s1= [2 3 4 5 6]
s2= [71 4 5 6 7 1 2 8 89 14 17 18]


对比
package main
import (
	"fmt"
)
func main(){
	data:=[]int{1,2,3,4,5,6,7,8,9,10}
	s1:=data[1:6]//从2开始
	s2:=data[2:7]//从3开始
	//s2=append(s2, 1,2,8,89,14,17,18)//超出容量会拷贝扩容
	s2[0]=71//这时候改的还是原来和S1共有的底层数组
	fmt.Println("s1=",s1)
	fmt.Println("s2=",s2)
	//s2=append(s2, 9,10,11,23,22,35,78,79,90,81,67,43,32,45,43,57,75,98,67,43,15,16,18,16,87,23,76,48,35,24,88,77)
	// fmt.Println("s1=",s1)
	// fmt.Println("s2=",s2)
}
执行结果
s1= [2 71 4 5 6]
s2= [71 4 5 6 7]

  示例

package main

import (
	"fmt"
)
func main(){
	var a []int
	for i:=0;i<20;i++{
		a=append(a, i)
		fmt.Printf("len:%d,cap:%d\n",len(a),cap(a))
	}
}
结果
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

  map 集合初始化与赋值

package main
import(
	"fmt"
)
func main(){
	//map 是key和value(值)的无序,注意查询方便
	var courseMap = map[string]string{
		"go":"go工程师",
		"python":"python工程师",
		"gin":"gin深入学习",
	}
	//打印map某值
	fmt.Println(courseMap["go"])
	//放值
	courseMap["mysql"]="mysql工程师"
	fmt.Println(courseMap)
	//声明没有初始化不能直接使用它是nil,要想往里面存值,必须要初始化操作故:var m1 map[string]string 操作失败
	var m1= map[string]string{} //{}里可以不写内存必须要有,代表初始化
	m1["s1"]="T"
	fmt.Println(m1)
	//使用make 方法初始化;make内置函数主要初始化切片、map、channel
	var m2= make(map[string]string,3)
	m2["1"]="a"
	fmt.Println(m2)
	//使用make方法初始化2
	var m3=make(map[string]string)
	m3["2"]="R"
	fmt.Println(m3)
}
执行结果
PS D:\golang\goproject\src\lnhgo> go run day02\demo06\main.go
go工程师
map[gin:gin深入学习 go:go工程师 mysql:mysql工程师 python:python工程师]
map[s1:T]
map[1:a]
map[2:R]

  map 的遍历

package main
import(
	"fmt"
)
func main(){
	//map 是key和value(值)的无序,注意查询方便
	var courseMap = map[string]string{
		"go":"go工程师",
		"python":"python工程师",
		"gin":"gin深入学习",
	}
	for i,v:=range courseMap{
		fmt.Println(i,v)
	}
	//只遍历key是还可以使用一下方式
	for v:=range courseMap{
		fmt.Println(v)
	}
}
执行结果
go go工程师
python python工程师
gin gin深入学习
go
python
gin
PS D:\golang\goproje

  判断map是否存在某个元素和删除元素

package main
import(
	"fmt"
)
func main(){
	//map 是key和value(值)的无序,注意查询方便
	var courseMap = map[string]string{
		"go":"go工程师",
		"python":"python工程师",
		"gin":"gin深入学习",
	}
	//判断map里是否存在key
	courseMap["java"]=""
	d,i:=courseMap["java"]//java 的value赋值给d
	if i{
		fmt.Println("map里存在java这个key")
		fmt.Println(d)
	}else{
		fmt.Println("map里不存在java这个key")
	}
	courseMap["java"]="java工程师"
	//d,i=courseMap["java"]//java 的value赋值给d
	if d,i=courseMap["java"]; i{
		fmt.Println("map里存在java这个key")
		fmt.Println(d)
	}else{
		fmt.Println("map里不存在java这个key")
	}
	//删除某个元素,删除不存在的key也不报错;map不是线程安全的
	delete(courseMap,"grpc")
	fmt.Println(courseMap)
	
}
执行结果
map里存在java这个key

map里存在java这个key
java工程师
map[gin:gin深入学习 go:go工程师 java:java工程师 python:python工程师]

  list 列表

package main

import (
	"container/list"
	"fmt"
)
func main(){
	var mylist list.List//初始化1
	//初始化2操作
//mylist := list.New()
	mylist.PushBack("go")
	mylist.PushBack("java")
	mylist.PushBack("PHA")
	//fmt.Println(mylist)
	//正向遍历列表
	for i:=mylist.Front();i!=nil;i=i.Next(){
		fmt.Println(i.Value)
	}
	//反向遍历
	for i:=mylist.Back();i!=nil;i=i.Prev(){
		fmt.Println(i.Value)
	}
	//方式2 初始化列表
	t:=list.New()
	t.PushBack("t")
	t.PushBack("grpc")
	//头部插入
	t.PushFront("JAVA")
	
	fmt.Println("遍历T列表")
	for i:=t.Front();i!=nil;i=i.Next(){
		fmt.Println(i.Value)
	}
	//插入元素
	s := t.Front()
	for; s!=nil;s=s.Next(){
		if s.Value.(string)== "grpc" {
			break
		}
	}
	t.InsertBefore("python",s)
	fmt.Println("-----------------------")
	for i:=t.Front();i!=nil;i=i.Next(){
		fmt.Println(i.Value)
	}
	fmt.Println("-------------------")
	//删除元素
	for; s!=nil;s=s.Next(){
		if s.Value.(string)== "grpc" {
			break
		}
	}
	t.Remove(s)
	for i:=t.Front();i!=nil;i=i.Next(){
		fmt.Println(i.Value)
	}

}
执行结果
go
java
PHA
PHA
java
go
遍历T列表
JAVA
t
grpc
-----------------------
JAVA
t
python
grpc
-------------------
JAVA
t
python

  

 

posted @ 2026-06-10 16:36  烟雨楼台,行云流水  阅读(3)  评论(0)    收藏  举报