Go 灵活多变的切片Slice

我们知道数组定义好之后其长度就无法再修改,但是,在实际开发过程中,有时候我们并不知道需要多大的数组,我们期望数组的长度是可变的,

在 Go 中有一种数据结构切片(Slice) 解决了这个问题,它是可变长的,可以随时向Slice 里面添加数据。

1 什么是切片(Slice)

在 Go 源码中是这样定义切片的,源码地址:https://github.com/golang/go/blob/master/src/runtime/slice.go

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

从源码中我们可以看到 Slice 也是一种结构体,这个结构体的名字是:Slice,这个结构体包含三个属性:array、len、cap。

第1个属性是指向底层数组的指针(Pointer),指向数组中 Slice 开始的位置;

第2个属性是切片中元素的个数(len),也就是这个 Slice 的长度;

第3个属性是这个切片的容量(cap),也就是 Slice 从开始位置到底层数组最后位置的长度;

2 切片的创建

2.1 切片的创建方式有很多种,一个比较通用的创建方式,使用 Go 的内置函数 make() 创建

package main

import "fmt"

func main() {
    var s1 []int = make([]int,5,8)
    var s2 []int = make([]int,8)

    fmt.Println(s1, s2)
}

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
[0 0 0 0 0] [0 0 0 0 0 0 0 0]
[[email protected]_81_181_centos golang]#  

make() 函数创建切片,需要提供三个参数,切片的类型、切片的长度、切片的容量。其中第3个参数是可选的,如果第三个参数不提供的话,

则代表创建的是满容切片,也就是长度和容量相等。另外切片也可以通过类型自动推导,省去类型定义和 var 关键字。比如:

package main

import "fmt"

func main() {
    var s1 []int = make([]int, 5, 8)
    s2 := make([]int, 8)

    fmt.Println(s1, s2)
}

另外,我们可以使用 len()、cap() 函数获取切片的长度和容量

package main

import "fmt"

func main() {
   numbers := make([]int,3,5)

   printSlice(numbers)
}

func printSlice(x []int) {
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x), cap(x),x)
}  

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
len=3 cap=5 slice=[0 0 0]
[[email protected]_81_181_centos golang]# 

2.2 用已有的数组生成切片

package main

import "fmt"

func main() {
    // 1.通过数组生成切片
    // 定义一个数组
    arr1 := [8]int{1,2,3,4,5,6,7,8}
    fmt.Println(arr1)

    // 定义一个切片
    s1 := arr1[1:4]
    fmt.Println(s1)
}

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
[1 2 3 4 5 6 7 8]
[2 3 4]
[[email protected]_81_181_centos golang]#  

2.3 用已有的切片生成切片

package main

import "fmt"

func main() {
   s := []int{1,2,3}
   s1 := s[1:3]  // s1 为 [2,3]
   s2 := s1[1:2]
   fmt.Println(s2)
}

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
[3]
[[email protected]_81_181_centos golang]#

3 切片的初始化

使用 make() 函数创建的切片是零值切片,Go 语言还提供了另外一种创建切片的方法,允许我们给它赋初值,使用这种方式创建的切片

是满容的。

3.1 通过数组初始化切片:

s := []int{1,2,3,4,5}

直接初始化切片s

s := arr[:]

 初始化切片s,是数组 arr 的引用

s := arr[startIndex:endIndex]

 从已有数组中创建一个新的切片,新切片元素是从数组 arr 下标 startIndex 到 endIndex-1

s := [startIndex:]

 缺省 endIndex 表示一直取到数组的最后一个元素

s :=arr[:endIndex]

 缺省 startIndex 表示从数组的第一个元素开始取值

3.2 通过切片初始化切片

s1 := s[startIndex:endIndex]

4 切片的遍历

切片在遍历上的方式和数组是一样的,支持 for 和 使用 range 关键字遍历

package main

import "fmt"

func main() {
   s := []int{1,2,3,4,5}

   // for 遍历
   for i := 0;i < len(s);i++ {
       fmt.Println(s[i])
   }

   // 使用 range 关键字
   for index,value := range s {
       fmt.Println(index, value)
   }
}

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
1
2
3
4
5
0 1
1 2
2 3
3 4
4 5
[[email protected]_81_181_centos golang]#  

5 切片的扩容(追加)
   我们知道切片的长度是可变化的,这个可变其实就是追加操作(append)导致的,我们使用 append 操作追加元素到切片时,如果容

量不够,则会创建新的切片,意味着内存地址也会发生变化,如果底层数组没有扩容,那么追加前后的两个切片共享底层数组,当底

层数组是共享的,一个切片的内容变化会影响到另一个切片的内容。如果底层数组扩容了,那么追加前后的两个切片是不共享底层数组

的。

package main

import "fmt"

func main() {
  // 定义切片s1 且切片s1是满容的
  s1 := []int{1,2,3,4,5}
  // 打印切片s1
  fmt.Printf("内存地址:%p \t\t长度:%v \t\t容量%v \t\t包含的元素:%v\n",s1,len(s1),cap(s1),s1)

  // 对满容的切片追加元素
  s2 := append(s1,6)
  // 打印切片s2
  fmt.Printf("内存地址:%p \t\t长度:%v \t\t容量%v \t\t包含的元素:%v\n",s2,len(s2),cap(s2),s2)
  // 修改s2的值
  s2[1] = 10
  fmt.Println(s1,s2)

  // 对没有满容的切片追加元素
  s3 := append(s2,7)
  // 打印切片s3
  fmt.Printf("内存地址:%p \t\t长度:%v \t\t容量%v \t\t包含的元素:%v\n",s3,len(s3),cap(s3),s3)
  // 修改s3的值
  s3[1] = 20
  fmt.Println(s2,s3)
}

输出结果

[[email protected]_81_181_centos golang]# go run slice01.go
内存地址:0xc420010150 		长度:5 		容量5 		包含的元素:[1 2 3 4 5]
内存地址:0xc420042050 		长度:6 		容量10 		包含的元素:[1 2 3 4 5 6]
[1 2 3 4 5] [1 10 3 4 5 6]
内存地址:0xc420042050 		长度:7 		容量10 		包含的元素:[1 10 3 4 5 6 7]
[1 20 3 4 5 6] [1 20 3 4 5 6 7]
[[email protected]_81_181_centos golang]# 

我们可以看到输出结果中 s1、s2 的内存地址发生了变化,是因为我们将元素 6 追加至切片 s1 中,超过了切片的最大容量 5 ,会创

建一个新的切片并将 s1 原有的元素拷贝一份至新的切片中,并且我们修改 s2 的值,发现 s1 并没有发生变化,说明s1、s2不在共享底

层数组。

扩容后的切片 s2 容量为 10 ,我们再向 s2 追加元素 7 后,没有超过 s2 的最大容量,且s2、s3 的内存地址一致,并且修改 s3 的值,

s2也会发生变化,说明s2、s3 共享底层数组。

所以,初始化切片的时候给出了足够的容量,append 操作的时候不会创建新的切片。

这里可能还会有一个疑问,为什么追加一个元素后容量由原来的 5 变成了 10?这里牵涉到 Slice 的扩容机制,可以参考这篇文章写得非

常详细:http://blog.realjf.com/archives/217

原文地址:https://www.cnblogs.com/leeyongbard/p/10046550.html

时间: 2024-10-10 06:10:44

Go 灵活多变的切片Slice的相关文章

golang切片slice

切片slice是引用类型 len()函数获取元素的个数 cap()获取数组的容量 1.申明方式 (1)var a []int 与数组不同的是他不申明长度(2)s2 := make([]int, 3, 10) //元素的类型,元素的数量,元素的容量  fmt.Println(len(s2), cap(s2)) 输出元素的数量和容量 2.讲数组转换成切片 2 a := [10]int{} 3 fmt.Println(a) 4 s1 := a[:10] //取前10个元素 [5:]取 5-最后的元素

Go语言之切片Slice练习

切片Slice理论知识 其本身并不是数组,它指向底层的数组 作为编程数组的替代方案,可以关联底层数组的局部或者全部 为引用类型 可以直接创建或从底层数组获取生成 使用len()获取元素个数,cap()获取容量 一般使用make()创建 如果多个slice指向相同底层数组,其中一个的值改变会影响全部 make([]T, len, cap) 其中,cap可以省略,则和len的值相同 len表示存数的元素个数,cap表示容量 slice与底层数组的关系 Reslice Reslice时索引以被slic

Golang 入门 : 切片(slice)

切片(slice)是 Golang 中一种比较特殊的数据结构,这种数据结构更便于使用和管理数据集合.切片是围绕动态数组的概念构建的,可以按需自动增长和缩小.切片的动态增长是通过内置函数 append() 来实现的,这个函数可以快速且高效地增长切片,也可以通过对切片再次切割,缩小一个切片的大小.因为切片的底层也是在连续的内存块中分配的,所以切片还能获得索引.迭代以及为垃圾回收优化的好处.本文将介绍 Golang 切片的基本概念和用法,演示环境为 ubuntu 18.04 & go1.10.1. 切

(一)Python入门-2编程基本概念:16字符串-切片slice操作-逆序

一:字符串切片slice操作 切片 slice 操作可以让我们快速的提取子字符串.标准格式为: [起始偏移量start:终止偏移量end:步长 step] 典型操作(三个量为正数的情况)如下: 操作和说明 示例 结果 [:] 提取整个字符串 “abcdef”[:] “abcdef” [start:]从start 索引开始到结尾 “abcdef”[2:] “cdef” [:end]从头开始直到end-1 “abcdef”[:2] “ab” [start:end]从start 到 end-1  “a

数组array和切片(slice)的区别

一 数组简介: 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其长度.当作为方法的参数传入时将复制一份数组而不是引用同一指针.数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度.注意:和C中的数组相比,又是有一些不同的 1. Go中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份2. 如果Go中的数组作为函数的参数,那么实际传递的参数是

Python 切片(slice)的扩展使用方法

# -*- coding: utf-8 -*- __author__ = 'xy' #切片(slice)的扩展使用方法 #1 用步长索引来进行扩展的切片操作 # slice[1:2:3]其中第3个位置表示步长,默认为1 #简单的str翻转: s = 'abcde' print s[::-1] # >>>'edcba' #间隔一个字符取值: print s[::2] # >>>'ace' #2 切片索引不越界 print ('a', 'b', 'c')[-100:100]

JS的数组进行切片slice

代码 var arr = new Array(6) arr[0] = "George" arr[1] = "John" arr[2] = "Thomas" arr[3] = "James" arr[4] = "Adrew" arr.slice(0,3) 和pyhon一样,左包含,又不包含,字符串也支持,因为字符串是特殊的数组 原文地址:https://www.cnblogs.com/robinunix/p/

Python~切片Slice

      [:] [:10] [-10:]       [':10:2]                                  

Go语言切片初识

Go切片(Slice)是Go数组的一个抽象. 由于Go数组允许定义类型的变量,可以容纳相同数据类型的几个数据项,但它不提供任何内置的方法来动态增加其大小或获取自己的子数组.切片就没有这样的限制. 它提供了数组所需的许多实用功能,并广泛用于Go编程. 定义切片     var numbers []int /* 未指定大小 */     /* numbers == []int{0,0,0,0,0}*/     numbers = make([]int,6,6) /* 长度是6容量是6的切片*/ le