golang的复合数据类型:数组、slice、map、结构体。数组和结构体是聚合类型。它们的值由内存中的一组变量构成。数组元素具有相同的类型,而结构体可以不同。数组和结构体的长度都是固定的。slice和map是动态数据结构。
数组
var a [3]int
name [nums]type
for i,j in := range a {
fmt.printf("%d %d\n",i j)
}
r := [...]int{99 :-1}
数组长度位置出现省略号表示由初始化元素个数来确定长度。
数组长度是类型的一部分(不同长度类型不同编译会发生报错),数组长度必须是常量表达式。
如果一个数组元素类型可以比较,那数组之间可以比较。
使用数组指针是高效的。
slice
一个拥有相同类型元素的可变长序列。通常写作[ ]T。它看上去像没有长度的数组类型。切片是一个很小的对象,对底层数组 进行了抽象。
轻量级数据结构,可以用来访问数组部分或全部元素,而这个数组称为slice的底层数组。
slice三个特性:长度 容量 指针。指针指向数组的第一个可访问的元素。
因为slice包含了指向数组的指针,所以将一个slice传递给函数的时候,可以在函数内部修改底层数组的元素。
创建和初始化:make([]type,len,cap)
。如果只有len,则len=cap。
另一种是切片字面量声明:slice := [ ]string{"1","2","3"}
切片也可以像数组一样使用索引声明slice := []int{99 :-1}
空切片和nil切片
声明后不作任何初始化,就是一个nil切片。var slice []int 指针为nil,len和cap都是0。
空切片 make创建或直接声明。具有指针地址。
多个切片共享一个数组。如果一个切片修改了底层数组的共享部分,另外的切片也会感知到。
切片使用append函数增加元素
append(slice,vaule),一个切片和被追加的值。长度改变容量可能会改变(取决于可用容量cap)。如果切片的底层数组容量也不够,将被引用的现在所有的值复制到新数组里,再追加新的值。
slice :=[]int{10,20,30,40} //len=cap=4
append(slice, 50)
//需要复制所有的值到新数组 len=4 cap=4*2
append扩展容量的算法:cap<1000成倍增加,大于1000,1.25倍增加。
创建切片时可以使用三个索引:slice := source[i:j:k]
source :=[]string{"a","b","c","d","e"}
slice := source[2:3:4]
slice从第三个元素开始切,长度为1,左闭右开,4为slice切片的终点。所以长度=j-i cap=k-i。
最后的索引不能越界(底层数组的容量)否则发生报错。
限制slice容量=长度的好处。当新切片第一次调用append时会创建一个新的数组,这个数组包括切片切的元素加追加的元素。,复制切片的元素再追加新元素,并返回一个引用了这个新底层数组的新切片。
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[2:3]
slice2 = append(slice2, 6)
fmt.Printf("%v", slice1)
fmt.Printf("%v", slice2)
}
运行结果:
切片中追加切片:使用…运算符
append(slice1,slice2… )
迭代切片
slice :=[]int{1,2,3,4,5}
for index,value range slice{
fmt.Printf("%d %d",index ,value)
]
第一个元素是索引,第二个元素是值的副本。要注意的是副本值的地址为同一变量的地址。如果不需要索引值可以通过_占位符;来替代。也可以传统for i ;i<len(slice);i++{}来迭代。
切片在函数间传递开销很小。
映射内部实现和基础功能 map
映射是一个集合,可以使用类似处理数组和切片的方式迭代映射中的元素。但映射是无序的集合,原理是使用了散列表。
初始化:
dict = make(map([string]int))
dict :=map[string]string{"red":"qwe","yellow":"asdasd","green":"gbfdgh"}
创建的时候,键可以试任何值。内置类型或结构类型,只要这个值可以使用==做比较。
map用delete(name,key)删除。map可在函数建传递映射。
结构体
编译语言意味着,编译器需要在编译时知晓程序里每个值的类型。
值的类型给编译器提供两部分信息:分配多少内存给这个值;第二部分这段内存表示什么?
用户定义的类型
Golang允许用户定义类型。声明时,编译器会给这个新类型一个框架,告知必要的内存大小和表示信息。go中用户定义的类型方法有两种。最常用的事struct。
//声明一个结构体
type user struct{
name string
email string
ext int
privileged bool
}
声明之后,就可以使用这个类型创建值了。
var bill user //被初始化为零值(根据其内置类型)
结构体也可以字面量初始化。初始化声明时,字段顺序要保持一致。也可以使用其他结构类型声明字段
type admin struct {
person user
level string
}
fred := admin {
type user struct{
name: "lisa",
email: "@qq.com",
ext: 213,
privileged: true
},
level: "super"
}
基于内置类型的新类型
type Duration int64
但是并不能用int64类型的值赋给Duration 类型的变量,这两个是不同类型的,尽管新类型Duration 的基础类型是int64。
方法
方法能给用户定义的类型增加新的行为。方法实际上是函数,只是在声明时,在关键字func和方法名之间增加一个参数。
分为值接受和指针接受。
func (u user)notify(){
fmt.Printf("Sending User Email to %s<%s>\n",
u.name
u.eamil )
}
类型的本质
声明一个新类型之后,声明一个该类型的方法之前,要弄清:这个类型的本质是什么?弄清之后,根据自己的需求,我们要做值传递还是指针传递。传递的一致性很重要。
内置类型
数值、布尔、字符串。当值传递给方法或函数时,应该传递一个对应值的副本。如果你对这些值增删时。
引用类型
切片、通道、映射、接口、函数类型。当声明引用类型变量时,创建的变量被称作标头(header)值。它是一个包含一个指向底层数据结构的指针。每个引用类型还包含一组独特的字段,用于管理底层数据结构。
结构类型
结构类型可以用来描述一组数据值,这组值的本质即可以是原始的,也可以是非原始的。