08、切片基础操作 和 append copy

01、切片的定义、操作、嵌套切片、引用类型证明

--数组申明之后长度就不会再改变,但往往我们需要一个动态数组,长度固定的数组一般不使用

--切片:拥有相同元素的可变长度存储容器


--定义和操作的实例:
	// 切片定义的时[]中没有数字
	var s1 []int
	var s2 []string
	fmt.Println(s1, s2)
	fmt.Println(s1 == nil) // 为空表示没有开辟内存空间
	fmt.Println(s2 == nil)

	// 切片初始化赋值
	s1 = []int{1, 2, 3}
	s2 = []string{"nanjing", "huawei", "wuxi"}
	fmt.Println(s1, s2)
	fmt.Println(s1 == nil)
	fmt.Println(s2 == nil)

	// 求切片长度和容量 -- len()求长度  cap()求容量
	fmt.Printf("len(s1): %d  cap(s1): %d\n", len(s1), cap(s1))
	fmt.Printf("len(s2): %d  cap(s2): %d\n", len(s2), cap(s2))

	// 2、通过数组切片的方式获取切片,和python非常类似
	a1 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	// 数字切片的多种方式
	s3 := a1[0:4]
	fmt.Println(s3) // [1 2 3 4]
	s4 := a1[1:6]
	fmt.Println(s4) // [2 3 4 5 6]
	s5 := a1[:5]
	fmt.Println(s5) // [1 2 3 4 5]
	s6 := a1[3:]
	fmt.Println(s6) // [4, 5, 6, 7, 8, 9, 10]
	s7 := a1[:]
	fmt.Println(s7) // [1 2 3 4 5 6 7 8 9 10]

	// 3、切片的容量和大小:数组切片后看切片容量,一般和切片指针位置及数组最大长度有关
	fmt.Printf("len(s5): %d  cap(s5): %d\n", len(s5), cap(s5)) // len(s5): 5  cap(s5): 10
	fmt.Printf("len(s6): %d  cap(s6): %d\n", len(s6), cap(s6)) // len(s6): 7  cap(s6): 7
	fmt.Printf("len(s7): %d  cap(s7): %d\n", len(s7), cap(s7)) // len(s7): 10  cap(s7): 10

	// 4、切片再切片,并印证切片是引用类型会随着原始数组的值改变而改变
	s8 := s6[1:3]
	fmt.Printf("len(s8): %d  cap(s8): %d\n", len(s8), cap(s8)) // len(s8): 2  cap(s8): 6

	// 印证s6是引用类型
	fmt.Println(s6)  // [4, 5, 6, 7, 8, 9, 10]
	a1[3] = 15
	fmt.Println(s6)  // [15 5 6 7 8 9 10]

02、更加高级的切片定义:指定长度和容量

--make函数生成切片
    --make([]T, size, cap)

--make()函数使用示例:
	a := make([]int, 5, 10)
	fmt.Printf("value: %v len(a): %d  cap(a): %d \n", a, len(a), cap(a))
****************  输出结果  *******************
    value: [0 0 0 0 0] len(a): 5  cap(a): 10
--切片之间是不能比较的
--不能使用==操作符来判断两个切片是否含有全部相等元素
--切片唯一合法的比较操作是和nil比较
--一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0
--但我们不能说一个长度和容量都是0的切片一定是nil
--所以要判断一个切片是否是空的,要是用len(s) == 0来判断,不应该使用s == nil来判断

--代码示例:
    var s1 []int         //len(s1)=0;cap(s1)=0;s1==nil
    s2 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
    s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil

03、切片的赋值拷贝

--也就是说明是引用类型
    // 引用的赋值拷贝
	s3 := []int{1, 3, 5}
	s4 := s3
	fmt.Println(s3, s4)
	s3[0] = 1001
	fmt.Println(s3, s4)
****************  输出  ******************
[1 3 5] [1 3 5]
[1001 3 5] [1001 3 5]

04、切片的遍历

--两种遍历方式:
	// 引用的赋值拷贝
	s3 := []int{1, 3, 5}

	// 01、索引方式遍历
	for i := 0; i < len(s3); i++ {
		fmt.Println(s3[i])
	}

	// 02、for  range方式遍历
	for j, v := range s3 {
		fmt.Println(j, v)
	}

05、append 和 

--append使用示例:
    --记住一定要使用原来的切片变量对其进行接收

--代码示例:
	// 不同于python,直接s1[3]="guangzhou",是没有办法将元素插入的,因为容量只有3不能通过这种方式进行扩容
	// 如果是数组切片,造成实际长度小于容量是可以用上述的方法进行元素新增的,此时不涉及到扩容
	s2 := make([]string, 1, 10)
	fmt.Println(s2)
	s2[0] = "guangzhou"
	fmt.Println(s2)

	// append 使用:单个添加
	s1 := []string{"北京", "上海", "深圳"}
	fmt.Printf("s1= %v len(s1): %d  cap(s1): %d \n", s1, len(s1), cap(s1)) // s1 value: [北京 上海 深圳] len(s1): 3  cap(s1): 3
	// 通过append进行扩容,必须使用原来的变量接收返回值
	// 这里可以看到容量直接翻倍,说明扩容的时候不会按照新增的+原有的来计算,会直接分配多余的空间以免造成不停扩容的现象
	// 扩容策略:如果新申请的容量大于原来的二倍
	s1 = append(s1, "广州")
	fmt.Printf("s1= %v len(s1): %d  cap(s1): %d \n", s1, len(s1), cap(s1)) // s1 value: [北京 上海 深圳 广州] len(s1): 4  cap(s1): 6

	// append 使用:一次添加多个
	ss := []string{"南京", "成都", "苏州"}
	// 这里直接 s1 = append(s1, ss) 是错误的必须拆开添加
	// 在golang语言中...表示拆开元素逐个操作
	s1 = append(s1, ss...)
	fmt.Printf("s1= %v len(s1): %d  cap(s1): %d \n", s1, len(s1), cap(s1)) // s1 value: [北京 上海 深圳 广州] len(s1): 4  cap(s1): 6
--append扩容策略:
    --具体代码见:$GOROOT/src/runtime/slice.go文件
	newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		if old.len < 1024 {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				newcap += newcap / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}

    --首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。

    --否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),

    --否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)

    --如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。

    --需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如int和string类型的处理方式就不一样。

06、copy 

--copy: 复制粘贴
    --copy(新变量, 原始变量)  // 新变量必须使用make初始化,并分配长度和容量
    --copy不是引用复制,而是值赋值,因此原始值变化不影响当前值
    
--代码示例:
	a1 := []int{1, 2, 3}
	a2 := a1
	var a3 = make([]int, 3, 3)
	copy(a3, a1)
	fmt.Println(a1, a2, a3) // [1 2 3] [1 2 3] [1 2 3]
	a1[0] = 100
	fmt.Println(a1, a2, a3) // [100 2 3] [100 2 3] [1 2 3]

07、其他操作切片元素定义: 删除  插入

--删除指定为元素:
	a1 := []int{12, 34, 56, 78, 90, 21, 43, 65, 87, 99}

	// 删除位置3的数据
	a1 = append(a1[:3], a1[4:]...)
	fmt.Println(a1)  // [12 34 56 90 21 43 65 87 99]

08、切片 和 数组的一点小变化注意

--注意这里x1的值的变化:
	x1 := [...]int{1, 3, 5} // [1, 3, 5]
	s1 := x1[:]             // [1, 3, 5]

	// 1、切片不保存具体的值
	// 2、切片对应的是一个底层数组
	// 3、底层数组都是一块连续的内存
	fmt.Println(s1, len(s1), cap(s1)) // [1, 3, 5] 3 3
	s1 = append(s1[:1], s1[2:]...)    // [1, 5]  == s1的起始位置还是x1的起始位置,所以当s1变为[1, 5]时则说明x1[0] == 1 x1[1] == 5 x1[2] == 5
	fmt.Println(s1, len(s1), cap(s1)) // [1, 5] 3 3

	// 这里x1的值是重要的
	fmt.Println(x1) // [1, 5, 5]
--这一段代码非常有意思,总算找到了怎么转换int到string类型
	a := make([]string, 0, 10)
	for i := 0; i < 10; i++ {
		a = append(a, fmt.Sprintf("%v", i))
	}
	fmt.Println(a)  // [0 1 2 3 4 5 6 7 8 9]

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值