网站首页 > 技术文章 正文
容器类型
java
java中的容器类型常用的是List,Set,HashMap等。
在java中谈容器,一般指的是Collection和Map。数组不属于容器的范围。
但是go中我们说到容器类型,一般是说数组、切片和map
go的数组
go数组的两个特性:长度固定,元素类型相同。
var arrName [n]T //长度为n,类型是T
[n]T 是arrName的数组类型。也就是说如果两个数组类型的元素类型 T 与数组长度 N 都是一样 的,那么这两个数组类型是等价的,如果有一个属性不同,它们就是两个不同的数组类 型。
因为数组在定义时其类型和长度都是明确的,所以实际内存分配上,也是一块连续的,可容纳所有数据的内存。
// 数组的声明
var a [3]int // 默认初始化为int的零值
a[0] = 1
b := [3]int{1, 3, 5} // 声明同时初始化
c := [2][2]int{{1, 1}, {2, 2}} //多维数组
d := [...]int{1, 2, 3, 4, 5} // 不用写数组的长度
var e = [...]int{ // 稀疏数组
99: 39, // 将第100个元素(下标值为99)的值赋值为39,其余元素值均为0
}
Go 提供了预定义函数 len 可以用于获取一 个数组类型变量的长度,通过 unsafe 包提供的 Sizeof 函数,我们可以获得一个数组变量 的总大小
t.Log(len(d)) // 5
t.Log(unsafe.Sizeof(d)) // 40
切片slice
切片的定义和数组很像,仅仅是少了一个“长度”属性。切片的存在是为了解决数组的问题,数组长度固定,很不灵活。
var myslice = []int{1, 2, 3, 4, 5, 6}
使用内置的append函数添加元素
myslice = append(myslice, 100)
slice的实现
slice的底层结构是
type slice struct {
array unsafe.Pointer // 指向底层数组的指针
len int // 切片的长度,即切片中当前元素的个数;
cap int // 底层数组的长度,也是切片的最大容量,cap 值永远大于等于 len 值
}
每个新建的slice都会新建一个底层数组。数组的长度和切点初始元素的个数相同。
我们还有其他方法创建切片。
- 通过make 函数来创建切片,并指定底层数组的长度 c := make([]int, 3, 5) // 切点的len是3,cap是5,即底层数组的长度是5.如果不指定。默认cap = len
t.Log(len(c), cap(c))
- 在已有数组的基础上创建切片
- 采用 array[low : high : max]语法基于一个已存在的数组创建切片。这种方式被 称为数组的切片化 func TestArr2Slice(t *testing.T) {
month := [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
slc := month[3:6:9]
t.Log(slc) // [Apr May Jun]
t.Log(len(slc), cap(slc)) // 3 6
}len = high - lowcap = max - low。通常省略max,max默认是数组的长度注意1,现在这个切片slc是直接指向数组month的。也就是说slc的改变会直接改变数组month slc[0] = "4月"
t.Log(month) // [Jan Feb Mar 4月 May Jun Jul Aug Sep Oct Nov Dec]注意2,对一个数组可以创建多个切片。因为这些切片底层都是指向数组的。所以任意一个切片的改变都会影响其他切片。 slc2 := month[3:6]
t.Log(slc2) // [4月 May Jun]
t.Log(len(slc2), cap(slc2)) // 3 9
slc2[1] = "5月"
t.Log(month) // [Jan Feb Mar 4月 5月 Jun Jul Aug Sep Oct Nov Dec]
t.Log(slc) // [4月 5月 Jun] - 基于切片创建切片用法和基于数组创建切片一样,底层指向同一个数组,所以互相影响。
slice的动态扩容
slice相比array的特点就是不定长。当len == cap时,再对slice进行append,就会发生切片的动态扩容。
// 切片的容量是翻倍增加
func TestSliceGrowing(t *testing.T) {
s := []int{}
for i := 0; i < 20; i++ {
s = append(s, i)
t.Log(len(s), cap(s))
}
}
结果打印:
slice_test.go:49: 1 1
slice_test.go:49: 2 2
slice_test.go:49: 3 4
slice_test.go:49: 4 4
slice_test.go:49: 5 8
slice_test.go:49: 6 8
slice_test.go:49: 7 8
slice_test.go:49: 8 8
slice_test.go:49: 9 16
slice_test.go:49: 10 16
slice_test.go:49: 11 16
slice_test.go:49: 12 16
slice_test.go:49: 13 16
slice_test.go:49: 14 16
slice_test.go:49: 15 16
slice_test.go:49: 16 16
slice_test.go:49: 17 32
slice_test.go:49: 18 32
slice_test.go:49: 19 32
slice_test.go:49: 20 32
可以看到slice的容量cap时翻倍增加的。
动态扩容导致的与原数组的分割
前面说了,切片可以从数组创建。切片的修改会直接修改原数组。但是,切片是可以继续追加元素的,那么切片追加元素超出了原数组的最大边界会怎么样呢?
func TestArr2SliceOut(t *testing.T) {
month := [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
slc := month[3:6:9]
t.Log(slc) // [Apr May Jun]
t.Log(len(slc), cap(slc)) // 3 6
slc = append(slc, "7月")
t.Log(slc) // [Apr May Jun 7月]
t.Log(month) // [Jan Feb Mar Apr May Jun 7月 Aug Sep Oct Nov Dec]
slc = append(slc, "8月")
slc = append(slc, "9月")
slc = append(slc, "10月")
t.Log(slc) // [Apr May Jun 7月 8月 9月 10月]
t.Log(month)// [Jan Feb Mar Apr May Jun 7月 8月 9月 Oct Nov Dec]
}
还是用月份举例。slc切片出了4,5,6三个月。len是3,cap是6。
追加一个7月。此时还在slc的容量范围内,所以直接影响了原数组。
但是当追加到10月时,已经超出了slc的容量范围。此时会进行扩容。slc的扩容会创建一个新的数组,与原数组不在有关系。所以10月不会影响原数组。之后slc做的任何操作都与原数组无关。
同样的道理推广到多个切片指向同一个数组。当某一个切片发生扩容后,他便于其他切片不在指向同一数组,也就不会再相互影响了。
切片的扩容这里是经常埋坑的地方。
一定要清楚的认识到slice与底层数组的关系
猜你喜欢
- 2024-12-17 C语言实现推箱子游戏!(超简单详细)代码思路+源码分享
- 2024-12-17 学习笔记之C#基础——数组和集合 c#中数组用法
- 2024-12-17 新函数REDUCE来了!Excel中的最强辅助,太强大了
- 2024-12-17 一篇文章学会golang语法,golang简明教程快速入门
- 2024-12-17 深入理解 Golang 中的值类型和引用类型
- 2024-12-17 SpringBoot系列之数据库初始化-datasource配置方式
- 2024-12-17 C++ 创建数组和使用数组学习笔记 c++如何建立数组
- 2024-12-17 Java Map 中那些巧妙的设计 javamap的用法
- 2024-12-17 大数据开发基础之一维数组的定义、初始化及与二维数组的区别
- 2024-12-17 go语言结构体与初始化 go 结构体初始化
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)