简单
golang里的数组和切片有了解过吗
- 数组和切片都是拥有相同类型一组元素集合 
- 数组为固定长度,切片为可变长度 
- 数组不可扩容,切片可以,切片扩容,如果不足1024每次扩容为两倍扩容,如果高于1024,为1.25倍扩容 
- 切片实际底层指向的也是一个数组的指针,切片的底层的结构体可以看到,有 指针 容量长度 
- 数组是值类型,将一个数组赋值给另一个数组时,传递的是一份深拷贝,赋值和函数传参操作都会复制整个数组数据,会占用额外的内存;切片是引用类型,将一个切片赋值给另一个切片时,传递的是一份浅拷贝,赋值和函数传参操作只会复制len和cap,但底层共用同一个数组,不会占用额外的内存。 
for range (slice和map)时遇到的“坑”
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           s := []
       int{
       1, 
       2, 
       3}
      
     
- 
     
      
     
     
      
           m := 
       make(
       map[
       int]*
       int)
      
     
- 
     
      
     
     
          
       for i, v := 
       range s {
      
     
- 
     
      
     
     
              
       // m[i] = &v 这样是不行的,
      
     
- 
     
      
     
     
              
       // 因为for range 迭代过程是根据slice中的变量遍历出来的新变量,遍历出来的值都是同一地址。所以应该用一个变量进行接受,这样每个不同值的地址就不同了
      
     
- 
     
      
     
     
      
               n := v
      
     
- 
     
      
     
     
      
               m[i] = &n 
       //这样才是正确的姿势
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
           fmt.Println(s) 
       // [1 2 3]
      
     
- 
     
      
     
     
          
       for _, v := 
       range m {
      
     
- 
     
      
     
     
      
               fmt.Println(*v)
       // 1 2 3
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
       }
      
     
  多个切片如果共享同一个底层数组,这种情况下,对其中一个切片或者底层数组的更改,会影响到其他切片
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           slice1 := []
       string{
       "1", 
       "2", 
       "3", 
       "4", 
       "5", 
       "6", 
       "7", 
       "8", 
       "9", 
       "10", 
       "11", 
       "12"}
      
     
- 
     
      
     
     
      
           Q2 := slice1[
       3:
       6]
      
     
- 
     
      
     
     
      
           fmt.Println(Q2, 
       len(Q2), 
       cap(Q2)) 
       // [4 5 6] 3 9
      
     
- 
     
      
     
     
      
           Q3 := slice1[
       5:
       8]
      
     
- 
     
      
     
     
      
           fmt.Println(Q3, 
       len(Q3), 
       cap(Q3)) 
       // [6 7 8] 3 7
      
     
- 
     
      
     
     
      
           Q3[
       0] = 
       "Unknown"
      
     
- 
     
      
     
     
      
           fmt.Println(Q2, Q3) 
       // [4 5 Unknown] [Unknown 7 8]
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           a := []
       int{
       1, 
       2, 
       3, 
       4, 
       5}
      
     
- 
     
      
     
     
      
           shadow := a[
       1:
       3]
      
     
- 
     
      
     
     
      
           fmt.Println(shadow, a) 
       // [2 3] [1 2 3 4 5]
      
     
- 
     
      
     
     
      
           shadow = 
       append(shadow, 
       100)
      
     
- 
     
      
     
     
          
       // 会修改指向数组的所有切片
      
     
- 
     
      
     
     
      
           fmt.Println(shadow, a) 
       // [2 3 100] [1 2 3 100 5]
      
     
- 
     
      
     
     
      
       }
      
     
  使用 func copy(dst, src []Type) int 解决
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           slice1 := []
       string{
       "1", 
       "2", 
       "3", 
       "4", 
       "5", 
       "6", 
       "7", 
       "8", 
       "9", 
       "10", 
       "11", 
       "12"}
      
     
- 
     
      
     
     
      
           Q2 := 
       make([]
       string, 
       3)
      
     
- 
     
      
     
     
          
       copy(Q2, slice1[
       3:
       6])
      
     
- 
     
      
     
     
      
           fmt.Println(Q2, 
       len(Q2), 
       cap(Q2)) 
       // [4 5 6] 3 3
      
     
- 
     
      
     
     
      
           Q3 := 
       make([]
       string, 
       3)
      
     
- 
     
      
     
     
          
       copy(Q3, slice1[
       5:
       8])
      
     
- 
     
      
     
     
      
           fmt.Println(Q3, 
       len(Q3), 
       cap(Q3)) 
       // [6 7 8] 3 3
      
     
- 
     
      
     
     
      
           Q3[
       0] = 
       "Unknown"
      
     
- 
     
      
     
     
      
           fmt.Println(Q2, Q3) 
       // [4 5 6] [Unknown 7 8]
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           a := []
       int{
       1, 
       2, 
       3, 
       4, 
       5}
      
     
- 
     
      
     
     
      
           shadow := 
       make([]
       int, 
       2)
      
     
- 
     
      
     
     
          
       copy(shadow, a[
       1:
       3])
      
     
- 
     
      
     
     
      
           fmt.Println(shadow, a) 
       // [2 3] [1 2 3 4 5]
      
     
- 
     
      
     
     
      
           shadow = 
       append(shadow, 
       100)
      
     
- 
     
      
     
     
      
           fmt.Println(shadow, a) 
       // [2 3 100] [1 2 3 4 5]
      
     
- 
     
      
     
     
      
       }
      
     
  实现slice线程安全有两种方式
- 通过加锁实现slice线程安全,适合对性能要求不高的场景 
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
          
       var lock sync.Mutex 
       //互斥锁
      
     
- 
     
      
     
     
      
           a := 
       make([]
       int, 
       0)
      
     
- 
     
      
     
     
          
       var wg sync.WaitGroup
      
     
- 
     
      
     
     
          
       for i := 
       0; i < 
       10000; i++ {
      
     
- 
     
      
     
     
      
               wg.Add(
       1)
      
     
- 
     
      
     
     
              
       go 
       func(i int) {
      
     
- 
     
      
     
     
                  
       defer wg.Done()
      
     
- 
     
      
     
     
      
                   lock.Lock()
      
     
- 
     
      
     
     
                  
       defer lock.Unlock()
      
     
- 
     
      
     
     
      
                   a = 
       append(a, i)
      
     
- 
     
      
     
     
      
               }(i)
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
           wg.Wait()
      
     
- 
     
      
     
     
      
           fmt.Println(
       len(a)) 
       // 10000
      
     
- 
     
      
     
     
      
       }
      
     
  - 通过channel实现slice线程安全,适合对性能要求高的场景 
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           buffer := 
       make(
       chan 
       int)
      
     
- 
     
      
     
     
      
           a := 
       make([]
       int, 
       0)
      
     
- 
     
      
     
     
          
       // 消费者
      
     
- 
     
      
     
     
          
       go 
       func() {
      
     
- 
     
      
     
     
              
       for v := 
       range buffer {
      
     
- 
     
      
     
     
      
                   a = 
       append(a, v)
      
     
- 
     
      
     
     
      
               }
      
     
- 
     
      
     
     
      
           }()
      
     
- 
     
      
     
     
          
       // 生产者
      
     
- 
     
      
     
     
          
       var wg sync.WaitGroup
      
     
- 
     
      
     
     
          
       for i := 
       0; i < 
       10000; i++ {
      
     
- 
     
      
     
     
      
               wg.Add(
       1)
      
     
- 
     
      
     
     
              
       go 
       func(i int) {
      
     
- 
     
      
     
     
                  
       defer wg.Done()
      
     
- 
     
      
     
     
      
                   buffer <- i
      
     
- 
     
      
     
     
      
               }(i)
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
           wg.Wait()
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           fmt.Println(
       len(a)) 
       // 10000
      
     
- 
     
      
     
     
      
       }
      
     
  数组怎么转集合
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           m := 
       make(
       map[
       int]
       int)
      
     
- 
     
      
     
     
      
           arr := []
       int{
       1, 
       2, 
       3, 
       4, 
       5}
      
     
- 
     
      
     
     
          
       for i, v := 
       range arr {
      
     
- 
     
      
     
     
      
               m[i] = v
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
           fmt.Println(m)
      
     
- 
     
      
     
     
      
       }
      
     
介绍一下通道
- 通道用于协程之间数据的传递,通道有三种类型,读和写的单向通道和双向通道 
- 通道可以控制协程的并发数 
map取一个key,然后修改这个值,原map数据的值会不会变化
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           ma := 
       make(
       map[
       int]
       int)
      
     
- 
     
      
     
     
      
           ma[
       1] = 
       123
      
     
- 
     
      
     
     
      
           updateMapValue(ma)
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           fmt.Println(ma[
       1]) 
       //321
      
     
- 
     
      
     
     
      
       }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       func updateMapValue(ma map[int]int) {
      
     
- 
     
      
     
     
      
           ma[
       1] = 
       321
      
     
- 
     
      
     
     
      
       }
      
     
channel有缓冲和无缓冲在使用上有什么区别?
无缓冲的与有缓冲channel有着重大差别:一个是同步的 一个是非同步的
比如
ch1:=make(chan int) 无缓冲
ch2:=make(chan int,1) 有缓冲
ch1<-1 无缓冲的
不仅仅是 向 c1 通道放 1 而是 一直要有别的协程 <-ch1 接手了 这个参数,那么ch1<-1之后的代码才会继续执行下去,要不然就一直阻塞着
而 ch2<-1 则不会阻塞,因为缓冲大小是1 (其实是缓冲大小为0)只有当 放第二个值的时候 第一个还没被人拿走,这时候才会阻塞
打个比喻
无缓冲的 就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走。
无缓冲保证信能到你手上
有缓冲的 就是一个送信人去你家仍到你家的信箱 转身就走 ,除非你的信箱满了 他必须等信箱空下来。
有缓冲的 保证 信能进你家的邮箱
如何判断channel是否关闭
用 select 和 <-ch 来结合可以解决这个问题
ok的结果和含义:
true:读到数据,并且通道没有关闭。
false:通道关闭,无数据读到。
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
      
           ch := 
       make(
       chan 
       int, 
       10)
      
     
- 
     
      
     
     
          
       for i := 
       1; i <= 
       9; i++ {
      
     
- 
     
      
     
     
      
               ch <- i
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
          
       close(ch)
      
     
- 
     
      
     
     
          
       for i := 
       0; i < 
       10; i++ {
      
     
- 
     
      
     
     
              
       select {
      
     
- 
     
      
     
     
              
       case v, ok := <-ch:
      
     
- 
     
      
     
     
                  
       if ok {
      
     
- 
     
      
     
     
      
                       fmt.Println(v)
      
     
- 
     
      
     
     
      
                   } 
       else {
      
     
- 
     
      
     
     
      
                       fmt.Println(
       "关掉了")
      
     
- 
     
      
     
     
      
                   }
      
     
- 
     
      
     
     
              
       default:
      
     
- 
     
      
     
     
      
                   fmt.Println(
       "没啥事")
      
     
- 
     
      
     
     
      
               }
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
       }
      
     
  需要注意:
- case 的代码必须是 _, ok:= <- ch 的形式,如果仅仅是 <- ch 来判断,是错的逻辑,因为主要通过 ok的值来判断; 
- select 必须要有 default 分支,否则会阻塞函数,我们要保证一定能正常返回; 
- 写入channel的时候判断其是否已经关闭,此时如果 channel 关闭,写入时触发panic: send on closed channel 
make 与 new 的区别
简单的说,new只分配内存,make用于slice,map,和channel的初始化。
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
          
       var v *
       int
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           *v = 
       8
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           fmt.Println(*v)
      
     
- 
     
      
     
     
          
       // 会报错
      
     
- 
     
      
     
     
          
       // panic: runtime error: invalid memory address or nil pointer dereference
      
     
- 
     
      
     
     
      
       }
      
     
解决:
   
    - 
     
      
     
     
      
       func main() {
      
     
- 
     
      
     
     
          
       var v *
       int
      
     
- 
     
      
     
     
      
           v = 
       new(
       int)
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           *v = 
       8
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
           fmt.Printf(
       "%d\n", *v)
      
     
- 
     
      
     
     
      
       }
      
     
go语言的引用类型有什么?
- map:golang中map是一种无序的、键值对的集合,其是通过key检索数据,且key类似于索引,指向数据的值,golang中常使用hash表来实现map。 
- pointers:golang中golang是指计算机内存中变量所在的内存地址,使用pointers可以节省内存,但golang中pointers不能进行偏移和运算,只能读取指针的位置。 
- slice:golang中slice是对数组的抽象,相对于数组,slice的长度是不固定的,可以追加元素,且在追加元素时可以增大slice的容量。 
- channel:golang中channel是指管道,是一种用于实现并行计算方程间通信的类型,允许线程间通过发送和接收来传输指定类型的数据,初始值为nil。 
- interface:golang中interface是指接口,是一组方法签名的集合,可以使用接口来识别一个对象够进行的操作。 
- function:golang中function是指函数,function不支持嵌套、重载和默认参数,但无需声明原型,常使用func关键字定义函数。 
转载:https://blog.csdn.net/qq_42490050/article/details/128571402
 
					