go基础系列:数组
了解Python、Perl、JavaScript的人想必都知道它们的数组是动态的,可以随需求自动增大数组长度。但Go中的数组是固定长度的,数组一经声明,就无法扩大、缩减数组的长度。但Go中也有类似的动态"数组",称为slice数据结构,在下一篇文章会详细解释它。
Go中的数组是slice和map两种数据类型的基础,这两种数据类型的底层都是通过数组实现的。
数组的存储方式
当在Go中声明一个数组之后,会在内存中开辟一段固定长度的、连续的空间存放数组中的各个元素,这些元素的数据类型完全相同,可以是内置的简单数据类型(int、string等),也可以是自定义的struct类型。
- 固定长度:这意味着数组不可增长、不可缩减。想要扩展数组,只能创建新数组,将原数组的元素复制到新数组
- 连续空间:这意味可以在缓存中保留的时间更长,搜索速度更快,是一种非常高效的数据结构,同时还意味着可以通过数值index的方式访问数组中的某个元素
- 数据类型:意味着限制了每个block中可以存放什么样的数据,以及每个block可以存放多少字节的数据
例如,使用下面的语句声明一个长度为4的int类型的数组,那么这个数组最多只能存放4个元素,且所有元素都只能是int类型。同时,还为这个数组做了初始化。
arr_name := [4]int{3,5,22,12}
这个数组的结构如下图所示:
其中左上角的小格子中的数表示各元素所在数组中的位置,也就是它们对应的index,index从0开始计算。
声明、初始化和访问数组
因为Go中的数组要求数据类型固定、长度固定,所以在声明的时候需要给定长度和数据类型。
例如声明一个长度为5、数据类型为int的数组,名为arr_name。
var arr_name [5]int
必须注意,虽然我们称呼数组为int类型的数组,但数组的数据类型是两部分组成的[n]TYPE
,这个整体才是数组的数据类型。所以,[5]int
和[6]int
是两种不同的数组类型。不同数据类型,意味着如果数组赋值给另一数组时需要数据类型转换操作,而Go默认是不会进行数据类型转换的。
在Go中,当一个变量被声明之后,都会立即对其进行默认的赋0初始化。对int类型的变量会默认初始化为0,对string类型的变量会初始化为空"",对布尔类型的变量会初始化为false,对指针(引用)类型的变量会初始化为nil。
数组也是一种变量类型,也会被初始化。初始化的方式是数组中的所有元素都根据数据类型赋值0。例如int类型的数组,元素全部赋值为0,string类型的数组,元素全部赋值为""等。
所以,上面声明数组arr_name之后,它初始化后的结果如下:
可以直接输出数组:
import "fmt"
var new_arr [3]int
fmt.Println(new_arr) // 输出:[0 0 0]
可以将数组的声明和初始化为给定值的操作合并:
arr_name := [5]int{3,5,22,12,23}
如果将元素个数指定为特殊符号...
,则表示通过初始化时的给定的值个数来推断数组长度:
// 声明长度为3的数组
arr_name1 := [...]int{2,3,4}
// 声明长度为4的数组
arr_name2 := [...]int{2,3,4,5}
如果声明数组时,只想给其中某几个元素初始化赋值,则使用索引号:
arr_name := [5]int{1:10, 2:20}
这表示声明长度为5的数组,但第2个元素的值为10,第3个元素的值为20,其它的元素(第1、4、5个元素)都默认初始化为0。
这个数组声明后的结果如下:
要访问数组中的某个元素,可以使用索引:
arr_name := [5]int{2,3,4,5,6}
// 访问数组的第4个元素,将输出:5
print(arr_name[3])
// 修改数组第3个元素的值
arr_name[2] = 22
指针数组(引用)
可以声明一个指针类型的数组,这样数组中就可以存放指针。注意,指针的默认初始化值为nil。
例如,创建一个指向int类型的数组:
arr_name := [5]*int{1:new(int), 3:new(int)}
上面的*int
表示数组只能存储*int
类型的数据,也就是指向int的指针类型。new(TYPE)
函数会为一个TYPE类型的数据结构划分内存并做默认初始化操作,并返回这个数据对象的指针,所以new(int)表示创建一个int类型的数据对象,同时返回指向这个对象的指针。
初始化后,它的结构如下:注意int指针指向的数据对象会被初始化为0。
对数组中的指针元素进行赋值:
package main
import "fmt"
func main() {
arr_name := [5]*int{1:new(int), 3:new(int)}
*arr_name[1]=10
*arr_name[3]=30
// 赋值一个新元素
arr_name[4]=new(int)
fmt.Println(*arr_name[1])
fmt.Println(*arr_name[3])
fmt.Println(*arr_name[4])
}
数组拷贝
在Go中,由于数组算是一个值类型,所以可以将它赋值给其它数组。
因为数组类型的完整定义为[n]TYPE
,所以数组赋值给其它数组的时候,n和TYPE必须相同。
例如:
// 声明一个长度为5的string数组
var str_arr1 [5]string
// 声明并初始化另一个string数组
str_arr2 := [5]string{"Perl","Shell","Python","Go","Java"}
// 将str_arr2拷贝给str_arr1
str_arr1 = str_arr2
数组赋值给其它数组时,实际上是完整地拷贝一个数组。所以,如果数组是一个指针型的数组,那么拷贝的将是指针数组,而不会拷贝指针所指向的对象。
package main
import "fmt"
func main() {
var str_arr1 [3]*string
str_arr2 := [3]*string{
new(string),
new(string),
new(string),
}
*str_arr2[0] = "Perl"
*str_arr2[1] = "Python"
*str_arr2[2] = "Shell"
// 数组赋值,拷贝指针本身,而不拷贝指向的值
str_arr1 = str_arr2
// 将输出Python
fmt.Println(*str_arr1[1])
}
拷贝后,它的结构如下:
array遍历迭代
range关键字可以对array进行迭代,每次返回一个index和对应的元素值。可以将range的迭代结合for循环对array进行遍历。
package main
func main() {
my_arr := [4]int{11,22,33,44}
for index,value := range my_arr {
println("index:",index," , ","value",value)
}
}
输出结果:
index: 0 , value 11
index: 1 , value 22
index: 2 , value 33
index: 3 , value 44
传递数组参数给函数
Go中的传值方式是按值传递,这意味着给变量赋值、给函数传参时,都是直接拷贝一个副本然后将副本赋值给对方的。这样的拷贝方式意味着:
- 如果数据结构体积庞大,则要完整拷贝一个数据结构副本时效率会很低
- 函数内部修改数据结构时,只能在函数内部生效,函数一退出就失效了,因为它修改的是副本对象
数组同样也遵循此规则。对于数组的赋值,上面数组拷贝中已经解释过了。如果函数的参数是数组类型,那么调用函数时传递给函数的数组也一样是这个数组拷贝后的一个副本。
例如,创建一个100W个元素的数组,将其传递给函数foo():
var big_arr [1e6]int
func foo(a [1e6]int) {
...
}
// 调用foo
foo(bigarr)
当上面声明big_arr后,就有100W个元素,假设这个int占用8字节,整个数组就占用800W字节,大约有8M数据。当调用foo的时候,Go会直接复制这8M数据形成另一个数组副本,并将这个副本交给foo进行处理。在foo中处理的数组,实际上是这个副本,foo()不会对原始的big_arr产生任何影响。
可以将数组的指针传递给函数,这样指针传递给函数时,复制给函数参数的是这个指针,总共才8个字节(每个指针占用1个机器字长,64位机器上是64bit共占用8字节),复制的数据量非常少。而且,因为复制的是指针,foo()修改这个数组时,会直接影响原始数组。
var big_arr [1e6]int
// 生成数组的指针
ref_big_arr := &big_arr
func foo(ra *[1e6]int) {
...
}
// 调用foo,传递指针
foo(ref_big_arr)
多维数组
可以通过组合两个一维数组的方式构成二维数组。一般在处理具有父、子关系或者有坐标关系的数据时,二维数组比较有用。
例如,声明二维数组:
var t_arr [4][2]int
这表示数组有4个元素,每个元素都是一个包含2元素的小数组。换一种方式,例如:
t_arr := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
还可以指定位置进行初始化:
t_arr := [4][2]int{1: {20, 21}, 3: {40, 41}}
t_arr := [4][2]int{1: {0: 20}, 3: {1: 41}}
go基础系列:数组的更多相关文章
- Java基础系列 - 数组、二维数组、对象数组
package com.test2; public class demo2 { public static void main(String[] args) { /** * 一维数组使用 */ //数 ...
- javascript基础系列(入门前须知)
-----------------------小历史---------------------------- javascript与java是两种语言,他们的创作公司不同,JavaScript当时是借 ...
- C#基础系列——小话泛型
前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 .这一章来总结下C#泛型技术的使用.据博主的使用经历,觉得泛型也是为了重用而生的,并且 ...
- mongodb基础系列——数据库查询数据返回前台JSP(一)
经过一段时间停顿,终于提笔来重新整理mongodb基础系列博客了. 同时也很抱歉,由于各种原因,没有及时整理出,今天做了一个demo,来演示,mongodb数据库查询的数据在JSP显示问题. 做了一个 ...
- 【JavaScript基础系列】决定你的人生能走多远的,是基础。
前言 javaScript门槛非常低,一点语法,一个dom,一个bom就可以使用它开发大部分js应用,再加上现在层出不穷的框架极大的简化抽象了javaScript的使用方式,但是我们始终不能忘记的一点 ...
- Java基础系列-ArrayList
原创文章,转载请标注出处:<Java基础系列-ArrayList> 一.概述 ArrayList底层使用的是数组.是List的可变数组实现,这里的可变是针对List而言,而不是底层数组. ...
- Java基础系列--HashMap(JDK1.8)
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10022092.html Java基础系列-HashMap 1.8 概述 HashMap是 ...
- JavaScript基础系列
JavaScript基础系列 JavaScript是一种基于对象和事件驱动的客户端脚本语言. JavaScript的注释 // 单行 /**/ 多行注释 JavaScript变量,函数名和操作符都是区 ...
- JVM基础系列第14讲:JVM参数之GC日志配置
说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志.而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数.今天就让 ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
随机推荐
- Github使用: 本地上传, 与之同步
很久不用Github 又忘记了怎么同步了, Git桌面前年已经部署好了. 1. 打开GitHub Desktop -- file -- clone repository --- 操作其中想要的一个文件 ...
- getElementById和$()获取值一点注意事项
<script type="text/javascript"> window.onload = function () { var obj = document.get ...
- noip第30课资料
- jQuery-少见获取元素的方式
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- spring 框架学习网站
spring 框架学习网站 NO1 http://www.mkyong.com NO2 https://spring.io/docs/reference
- java maven web 项目启动之后,访问所有页面为空白,不是404!!!
自己解决了大半天,后面通过解决spring单元测试的时候,发现单元测试可以用了,项目启动也可以访问页面了,具体原因不太清楚 可能原因: (1)pom.xml 依赖有重复的地方 (2)不排除与公司内网有 ...
- Python 基础整理(未完)
数据类型和变量: 整数:Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如:1,100,-8080,0,等等. 计算机由于使用二进制,所以,有时候用十六 ...
- CLion之C++框架篇-安装工具,基础框架的搭建(一)
背景 日常学习C++,也就是看看书.在vim里写写代码.在日常项目开发中,也是边看书(一是系统性理解.二是找找有什么更好的代码编写方式)边写代码,会顺带看看别人的代码怎么写的? 日常学 ...
- .net的服务转移
问题: 服务器换新,但是本来服务器部署了一些window服务,需要迁移过来 解决过程: 百度了估计几百页了,之前因为老机器挂了,写这个代码布服务的人也早就不在了,所以自己闷头苦找,一个java初级程序 ...
- Python的基础语法(一)
0. 前言 最近正在重新整理Python的基础知识,以便更好地学习新知识.这一部分主要是讲述Python基础语法的演示.下面的语法都是基于Python3的语法. 1. 注释 注释:Python的注释方 ...