golang中有数组和Slice两种数据结构:

  • 数组

    一种具有固定长度的基本数据结构,一旦创建了它的长度就不允许改变,数组的空余位置用缺省值填补,不允许数组越界。

  • Slice

    基于数组的实现,是长度动态不固定的数据结构,本质上是一个对数组字序列的引用,提供了对数组的轻量级访问。

数组

1.创建数组:

func main() {
var arr1 = [...]int{1,2,3,4} //[...]默认为元素的数量即为数组的长度
fmt.Println(len(arr1)) //4
// arr1[4] = 5 //panic 数组越界
fmt.Println(arr1) //[1 2 3 4]
var arr2 = [10]int{1,2,3,4}
fmt.Println(arr2) //[1 2 3 4 0 0 0 0 0 0]
}

2.数组是值拷贝传递:

func main() {
var arr = [10]int{4,5,7,11,8,9}
fmt.Println(arr) //[4,5,7,11,8,9,0,0,0,0]
//验证数组是值拷贝传递
AddOne(arr)
fmt.Println(arr) //[4,5,7,11,8,9,0,0,0,0]
}
func AddOne(arr [10]int){
arr[9] = 999999
fmt.Println(arr) //[4,5,7,11,8,9,0,0,0,999999]
}

切片(slice)

1.首先看看slice的源码结构:

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

slice是一个特殊的引用类型,但是它自身也是个结构体

  • 属性len表示可用元素数量,读写操作不能超过这个限制,不然就会panic
  • 属性cap表示最大扩张容量,当然这个扩张容量也不是无限的扩张,它是受到了底层数组array的长度限制,超出了底层array的长度就会panic

2.slice的创建:

func main() {
var arr = [...]int{0,1,2,3,4,5,6}
slice1 := arr[1:4:5] //{low:high:max} 最多再扩张一个元素
//max超出 len(arr)
//slice2 := arr[1:4:7] //panic
fmt.Println(slice1) //[1,2,3]
slice3 := slice1[1:3:4] //[2,3] 大于4会panic
fmt.Println(slice3)
slice4 := slice1[1:4] //[2,3] 大于4会panic
fmt.Println(slice4) //[2 3 4] 出现slice1中没有的元素
}

上面代码中创建了一个长度为7的数组arr,同时创建一个基于数组arr的切片slice1,切片引用了数组的index=1到index=3之间的元素,同时也允许切片最大扩张1个元素大小的空间。如果这个扩张空间大于7那么程序就会panic。最后创建了一个基于slice1延申的一个切片slice2,它引用了切片的index=1到index=3之间的元素,由于slice1最大扩容1个元素,因此slice2也最多扩容一个元素,超过了会panic。

  • 创建基于底层数组的slice,其cap取值在: len<=cap<=len(arr)之间
  • 创建基于一个切片的slice,其cap取值在: len(slice1)<=cap<=cap(slice1)之间

3.slice使用make创建

func main() {
var slice = make([]int,3,5) //len=3,cap=5
fmt.Println(slice) //[0,0,0]
slice2:=slice[:5] //slice实现了对slice的扩容,切片长度变为5
fmt.Println(slice2) //[0,0,0,0,0]
}

4.切片作为参数传递

func main() {
var slice = make([]int,3,5) //len=3,cap=5
fmt.Println(slice) //[0,0,0]
slice2:=slice[:5] //slice实现了对slice的扩容,切片长度变为5
fmt.Println(slice2) //[0,0,0,0,0]
slice[0] = 999 //这里slice和slice的index=0位置都是999 因为他们引用的底层数组的index=0位置都是999
fmt.Println(slice)
fmt.Println(slice2)
AddOne(slice) //[8888,0,0]
fmt.Println(slice) //[8888,0,0]
fmt.Println(slice2) //[8888,0,0,0]
}
func AddOne(s []int){
s[0] = 8888
fmt.Println(s)
}
  • 因为切片是个引用类型,所以它作为参数传递给函数,函数操作的实质是底层数组

5.Golang中的切片追加append()

func main() {
var arr = [...]int{1,2,3,4}
fmt.Println(arr) //[1,2,3,4]
slice := arr[:]
fmt.Println(slice) //[1,2,3,4]
slice = append(slice,[]int{5,6,7}...) //此时slice的引用地址已经发生改变了,它引用的底层数组再也不是arr了,而是一个新的数组newarr[1,2,3,4,5,6,7]
fmt.Println(slice) //[1,2,3,4,5,6,7]
slice = append(slice, 8) //[1,2,3,4,5,6,7,8]
//验证slice引用的地址已经发生改变
slice[0] = 666
fmt.Println(arr) //[1,2,3,4]
fmt.Println(slice) //[666,2,3,4,5,6,7,8]
}
  • 这里由于slice进行追加的元素超出了原来数组的大小,因此go内部会帮我们创建一个新的底层数组,而slice的引用地址不再是arr了,变成了新创建的数组。

还有一种情况就是当slice进行追加的时候没有超出原来数组的大小的时候,其引用地址没有发生改变。

func main() {
var arr = [6]int{1,2,3,4}
fmt.Println(arr) //[1,2,3,4,0,0]
slice := arr[:4]
fmt.Println(slice) //[1,2,3,4]
slice = append(slice,5)
fmt.Println(arr) //[1,2,3,4,5,0]
fmt.Println(slice) //[1,2,3,4,5]
}

总结

  • (1)go是有数组的,只是平时用切片比较多。数组大小一旦创建就不能改变,数组长度大于元素个数的时候会用0补位,这跟其他语言是相通的。
  • (2)切片slice可以看作是对数组的一切操作,它是一个引用数据类型,其数据结构包括底层数组的地址,以及元素可操作长度len或可扩容长度cap。
  • (3)要想突破slice的扩容cap限制进行无限扩容就需要使用append()函数进行操作。如果append追加的元素后slice的总长度不超过底层数组的总长度,那么slice引用的地址不会发生改变,反之引用地址会变成新的数组的地址。
  • (4)slice是一个抽象的概念,它存在的意义在于方便对一个顺序结构进行一些方便操作,例如查找,排序,追加等等,这个类似于python的list。

[Golang]-1 Slice与数组的区别的更多相关文章

  1. go slice和数组的区别

    1.使用方式 数组和slice长的很像,操作方式也都差不多,并且slice包含了数组的基本的操作方式,如下标.range循环,还有一些如len()则是多种类型共用,所以根据操作根本搞不清数组和切片的区 ...

  2. 数组的常用方法concat,join,slice和splice的区别,map,foreach,reduce

    1.concat()和join() concat()是连对两个或两个数组的方法,直接可以将数组以参数的形式放入 join()是将数组中的所有元素放入一个字符串中,通俗点讲就是可以将数组转换成字符串 2 ...

  3. JavaScript中的数组与伪数组的区别

    在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function). 基本数据类型:String,boolean,Number,Undefined, Null 引用数 ...

  4. golang基础---Slice切片

    切片Slice在go语言中是单独的类型(指向底层的数组),不同于python(对可迭代对象操作的工具),注意区分数组和slice的区别 定义一个空slice,格式var s []int,这种既没有长度 ...

  5. split slice splice的简单区别

    split slice splice的简单区别 split: 分割 //字符串方法 string.split let str = 'hello world'; //str.split('') 以什么东 ...

  6. null和undefined的区别、数组和伪数组的区别

    null和undefined的区别 undefined: 字面意思是未定义的值,语义是希望表示一个变量最原始的状态,而非人为操作的结果.这种原始状态会在以下四个场景中出现: 声明了一个变量但没有赋值 ...

  7. C++中关于[]静态数组和new分配的动态数组的区别分析

    这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下 本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加 ...

  8. C-指针和数组的区别

    指针的操作: 允许:1)同类型指针的赋值 2)与整形的加减运算 3)指向同一数组内指针的减运算和比较 4)赋 ‘0’ 或与 ‘0’ 比较 不允许:1)两指针的相加,相乘除,位移或mask 2)与flo ...

  9. Java基础知识强化之集合框架笔记01:集合的由来与数组的区别

    1. 集合的由来: 我们学习的是面向对象语言,而面向对象语言对事物的描述是通过对象体现的,为了方便对多个对象进行操作,我们就必须把这多个对象进行存储.而要想存储多个对象,就不能是一个基本的变量,而应该 ...

随机推荐

  1. LeetCode202. 快乐数

    题目 编写一个算法来判断一个数 n 是不是快乐数. 快乐数定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1, 也可能是 无限循环 但始终变不到 ...

  2. CTFHub - Web(二)

    目录遍历: 法一: 依次查看目录即可: 法二: 利用脚本:  #!/usr/bin/python3  # -*- coding: utf-8 -*-  # --author:valecalida-- ...

  3. ctfhub技能树—文件上传—00截断

    什么是00截断 相关教程:http://www.admintony.com/%E5%85%B3%E4%BA%8E%E4%B8%8A%E4%BC%A0%E4%B8%AD%E7%9A%8400%E6%88 ...

  4. 1.5V升3.3V芯片电路图,稳压3.3V供电MCU模块等

    干电池1.5V可以升到3.3V,通过PW5100干电池升压IC,于外围3个元件:2个电容和一个电感即可组成1.5V升3.3V的电路系统. 干电池属于低能量的电池产品,不过一般使用到干电池的产品也是输出 ...

  5. MySQL全面瓦解21(番外):一次深夜优化亿级数据分页的奇妙经历

    背景 1月22号晚上10点半,下班后愉快的坐在在回家的地铁上,心里想着周末的生活怎么安排. 突然电话响了起来,一看是我们的一个开发同学,顿时紧张了起来,本周的版本已经发布过了,这时候打电话一般来说是线 ...

  6. 【链表】leetcode-1290-二进制链表转整数

    leetcode-1290-二进制链表转整数 题目描述 给你一个单链表的引用结点 head.链表中每个结点的值不是 0 就是 1.已知此链表是一个整数数字的二进制表示形式. 请你返回该链表所表示数字的 ...

  7. 现有以下关系型数据库中的表(见表4-20表4-21和表4-22),要求将具转换为适合Hbase存储的表并插入数据。

    ① createTable(String tableName, String[] fields) 创建表,参数tableName为表的名称,字符串数组fields为存储记录各个域名称的数组.要 求当H ...

  8. 网络编程中 TCP 半开连接和TIME_WAIT 学习

    https://blog.csdn.net/chrisnotfound/article/details/80112736 上面的链接就是说明来 SO_KEEPALIVE 选项 为什么还需要 在应用层开 ...

  9. LibreOJ #10047

    应同机房某大佬的要求来写这篇题解 Description 给定一个字符串 \(S\) 和一个数 \(K\),要求求出 \(S\) 的所有形似 \(A+B+A\) 的子串数量,其中 \(\mid A\m ...

  10. loj10157

    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫. 皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状,某些宫殿间可以互相望见.大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不 ...