
头条真实面试题
new 和 make 区别
make
返回的还是引用类型本身;而new
返回的是指向类型的指针。
make
也是用于内存分配的,但是和new
不同,它只用于chan
、map
以及slice
的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
注意,因为这三种类型是引用类型,所以必须得初始化,但是不是置为零值,这个和new
是不一样的。
二者都是内存的分配(堆上),但是make
只用于slice、map以及channel的初始化(非零值);而new
用于类型的内存分配,并且内存置为零。

数组 Arrays
数组是同一种数据类型的固定长度的序列。
数组是值类型,因此改变副本的值,不会改变本身的值;
当作为方法的入参传入时将复制一份数组而不是引用同一指针。
通过从0开始的下标索引访问元素值。
数组定义
var a []inta = make([]int, 5)var a1 [5]int = [5]int{1, 2, 3, 4, 5} //len:5 content:[1 2 3 4 5]var a2 = [5]int{1, 2, 3, 4, 5} //len:5 content:[1 2 3 4 5]var a3 = [...]int{1, 2, 3, 4, 5} //len:5 content:[1 2 3 4 5]var a4 = [...]int{1: 100, 2: 200} //len:3 content:[0 100 200]var a5 = [...]string{1: "nick", 2: "dawn"} //len:3 content:[ nick dawn]
数组定义后,长度不能变。
长度是数组类型的一部分,具有不同长度的数组,其类型是不同的。
因此,var a[5] int 和 var a[9]int 是不同的类型。
数组使用
通过下标访问,超出会报错
arr := [5]int{1, 2, 3}//fmt.Println(arr[5]) 报错
通过for遍历数组元素
func main() {arr := [5]int{1, 2, 3}for i:=0; i<len(arr); i++ {fmt.Println(arr[i]) //12300}for i,v := range arr{fmt.Printf("index[%d]:content[%v]\n", i, v)}}
值类型数组赋值,改变副本不会改变自身
func main() {arr := [5]int{1, 2, 3}arr2 := arrarr2[0] = 10fmt.Println(arr) //[1 2 3 0 0]fmt.Println(arr2) //[10 2 3 0 0]}
栗子 斐波那契数列
func test1(n int) {var a []inta = make([]int, n)a[0] = 1a[1] = 1for i := 2; i < n; i++ {a[i] = a[i-1] + a[i-2]}for _, v := range a {fmt.Println(v)}}
二维数组
多维数组,二维数组举例
var a1 [2][5]int
二维数组遍历
func main() {var a1 [2][5]int = [...][5]int{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}}for row, v := range a1 {for col, v1 := range v {fmt.Printf("(%d,%d)=%d ", row, col, v1)}fmt.Println()}}
切片 Slices
切片是长度可变、容量固定的相同的元素序列。
切片是数组的一个引用,因此切片是引用类型。
因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。
slice 与 array 接近,但是在新的元素加入的时候可以增加长度。slice 总是指向底层的一个 array。slice本身不是数组,slice 是一个指向 array的指针。
切片定义
创建切片跟创建数组唯一的区别在于 Type 前的“ [] ”中是否有数字,为空,则代表切片,否则则代表数组。
s1 := [] int{1, 2, 3} //直接初始化切片,[]表示是切片类型,初始化值依次是1,2,3.其cap=len=3s2 := arr[:] //初始化切片s2,是数组arr的引用s3 := arr[startIndex:endIndex] //将arr中从下标startIndex到endIndex-1下的元素创建为一个新的切片s4 := arr[startIndex:] //缺省endIndex时将表示一直到arr的最后一个元素s5 := arr[:endIndex] //缺省startIndex时将表示从arr的第一个元素开始s6 := s1[startIndex:endIndex] //通过切片s6初始化切片截取s1s7 := make([]int, len, cap) //通过内置函数make()初始化切片s,cap可以省略(省略时,值等同于len)
len() 与 cap()
长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。
容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。
arr := [5]int{1, 2, 3}fmt.Println(len(arr)) //5fmt.Println(cap(arr)) //5
切片使用
遍历及修改
for i, v := range slice0 {slice0[i] = strings.ToUpper(v)fmt.Printf("index[%d]content[%s,%s]\n", i, v, slice0[i])}
append 及 copy
append操作:slice可以在尾部追加元素,甚至还可以将一个slice追加到另一个slice的尾部,如果最终的长度没有超过原始的slice,那么append操作将返回原来的slice,否则将重新分配内存地址。
copy操作:copy操作返回复制的元素的个数,复制的数量是len(src)和len(dst)中最小的值。
slice := []int{1, 2}fmt.Printf("len[%d],content:%v\n", len(slice), slice) //len[2],content:[1 2]slice = append(slice, 5, 6, 8, 9)fmt.Printf("len[%d],content:%v\n", len(slice), slice) //len[6],content:[1 2 5 6 8 9]slicecp := make([]int, len(slice))fmt.Printf("len[%d],content:%v\n", len(slice), slice) //len[6],content:[1 2 5 6 8 9]n := copy(slicecp, slice)fmt.Printf("len[%d],content:%v, retN:%d\n", len(slice), slice, n) //len[6],content:[1 2 5 6 8 9], retN:6slicecp[0] = 10fmt.Printf("len[%d],content:%v\n", len(slice), slice) //len[6],content:[1 2 5 6 8 9]fmt.Printf("len[%d],content:%v\n", len(slicecp), slicecp) //len[6],content:[10 2 5 6 8 9]sliceN := append(slice, slicecp...)fmt.Printf("len[%d],content:%v\n", len(sliceN), sliceN) //len[12],content:[1 2 5 6 8 9 10 2 5 6 8 9]
值类型修改值会影响本身。
slice0 := []string{"a", "b", "c", "d", "e"}slice1 := slice0slice1[0] = "Nick"fmt.Println(slice0) //[Nick b c d e]fmt.Println(slice1) //[Nick b c d e]
内存布局与扩容
切片是引用类型,指针内部只向一个数组。
代码实现,内存地址表示是同一块地址。
func main() {var a []int = []int{1, 2, 3, 4, 5}s := a[1:]fmt.Printf("a=%p, s=%p \n", &(a[1]), s) //a=0xc420016188, s=0xc420016188s = append(s, 10, 10, 10)fmt.Printf("a=%p, s=%p \n", &a[1], s) //a=0xc420016188, s=0xc4200141c0}
切片的长度是可变的,那自动扩容是怎样的机制呢?
是 double(双倍),看下面代码。
unc main() {var a [5]int = [...]int{1, 2, 3, 4, 5}s := a[1:]fmt.Println(cap(s), len(s)) //4 4s = append(s, 10, 10, 10)fmt.Println(cap(s), len(s)) //8 7s = append(s, 10)fmt.Println(cap(s), len(s)) //8 8s = append(s, 10)fmt.Println(cap(s), len(s)) //16 9s = append(s, 10, 10, 10, 10)fmt.Println(cap(s), len(s)) //16 13s = append(s, 10, 10, 10, 10, 10, 10)fmt.Println(cap(s), len(s)) //32 19}
map
map在Go语言中是作为一种内建类型存在。
key-value的数据结构,又叫字典或关联数组。
map是引用类型。
map声明是不会分配内存的,需要make初始化。
map声明初始化
声明
var a map[keytype]valuetypevar a map[string]stringvar a map[string]intvar a map[int]stringvar a map[string]map[string]string //嵌套
声明并初始化
var a map[string]stringa = make(map[string]string, 10)a := make(map[string]string, 10)a := make(map[string]string)var a map[string]string = map[string]string{}var a map[string]string = map[string]string{"A": "A", //注意是逗号}
map 使用
增删改查
m["name"] = "Nick" "create"delete(m, "name") "delete"m["name"] = "Dawn" "update"name := m["name"] "read"
读取不异常
name, errbool := m["name"]if !errbool {m["name"] = "Nick"}
二维map
func modify(a map[string]map[string]string) {_, ok := a["name"]if !ok {a["name"] = make(map[string]string)}a["name"]["Nick"] = "suolong"a["name"]["Nicky"] = "manyRou"}func testMap3() {var a map[string]map[string]stringa = make(map[string]map[string]string, 10) //初始化一维a["name"] = make(map[string]string) //初始化二维modify(a)fmt.Println(a)}
slice of map
Items := make([]map[int][int], 5)For i := 0; i < 5; i++ {items[i] = make(map[int][int])}
map 排序
先获取所有key,把key进行排序
按照排序好的key,进行遍历
import "sort"func testMapSort() {var a map[int]inta = make(map[int]int, 5)a[8] = 10a[5] = 10a[2] = 10a[1] = 10a[9] = 10var keys []intfor k, _ := range a {keys = append(keys, k)}sort.Ints(keys)for _, v := range keys {fmt.Println(v, a[v])}}
map 反转
初始化另外一个map,把key、value互换即可
func testMapSort1() {var a map[string]intvar b map[int]stringa = make(map[string]int, 5)b = make(map[int]string, 5)a["name"] = 53a["ege"] = 10for k, v := range a {b[v] = k}fmt.Println(b)}





