参照Java学Go(三)—— go的内置类型:数组、切片slice、map

    前一篇说到了go的变量、常量和基本数据类型,接下来继续说一下go的其他内置类型:数组、切片(slice)和集合(map)

    一,数组

    go和java中肯定都有数组了,只不过在定定义上和定义变量类似,把类型放到了后边(还是觉得很难受有没有?)

    首先对比一下定义:

//java:
int[] arr;            //没有初始化
arr = new int[1]{};   //初始化长度为1的数组
int[] arr1 = new int[]{1,2,3};   //初始化三个值
//go:
var arr [1]int        //同样
arr = [1]int{}       //初始化长度为1的数组
arr1 := [3]int{1,2,3}          //同样初始化三个值
arr2 := [...]int{1,2,3}          //同样初始化三个值

    我们看到不光数组类型放到了变量后边,类型名称都放到了"[]"后边,和java完全是颠倒的(真的好别扭);

    注意,在初始化arr1的时候,go的[]中指定了3(也可以不写,但是不写就是切片了,之后会说到),但是在java中的[]中是不能指定的,否则会报错。

    再有,在go初始化arr2的时候,[]中写额是“...”,三个点表示自动计算长度,用户用户保持长度的正确性,但是这种只能是在静态初始化的时候(指定数组内容),如下也会报错的:
var arr2 [...]string //报错:use of [...] array outside of array literal
    数组在使用上也都一样,都是使用“[]”来对元素进行操作:
var arr [3]string = [3]string{"1","2","3"}  //固定长度
arr1 := [3]string{"1","2","3"}              //还是这样写简单
arr[0] = "3"
arr1[len(arr1)-1] = "1"
fmt.Println("arr=",arr)
fmt.Println("arr1=",arr1)

    数组长度也属于类型的一部分,所以我们看第一行,定义并初始化的时候,长度必须一致才可以,否则会报错,因为不同长度的数组是两个类型。

    再有数组的长度是不可变的。数组之间的赋值是值的赋值,当数组作为参数的时候,其实是数组的拷贝,不是指针,如果要使用指针,那就要使用切片(slice)了,这个后面就会说到。


    说到数组,当然不能只是一维,多维数组如何定义呢?跟java没两样,只是要记得go的定义风格就好了,请看:
arr_1 := [1][2]int{{1,2}}

    多维数组操作上就不多说了。


     二,切片(slice)

    切片也是go原生的类型,也是一种数组,叫动态数组,其实就相当于java中的ArrayList,可变数组。

    在说数组初始化的时候说到,数组的定义不分是包含长度的(中括号[]中的值),在定义的时候如果不写这个长度值,就相当于定义个一个切片(定义了长度就是数组),如:        

//go
var slice []int 
slice = []int{1,2,3} 
slice1 := [3]int{1,2,3}  
    可以看到除了[]中没有指定值外,和数组没有什么区别,但是在使用上就灵活很多,比如:
//go
slice := []int{1,2,3} 
slice2 := slice[0:2]  //从一个slice初始化,0:2表示从开始的位置截止到第三个。
slice2 = slice[:2]    //如果是从0开始,可以简写为[:2]
slice2 = slice[:]    //拷贝slice
  我们看到可以从一个slice截取一部分作为初始化和赋值,当然也可以使用这种方式来截取数组。

  另外切片和数组的不同是,切片其实是一种引用类型,底层都是一个数组的引用,所以在改变引用值得时候,切片的值也会改变的:

arr := [3]int{1,2,3} 
slice = arr[:]    //copy arr
arr[0] = 10 
fmt.Println("arr=",arr)       //[10,2,3]
fmt.Println("slice=",slice) //[10,2,3]
    在使用切片的时候,还经常会用到几个内置函数:
  •     cap 得到slice的最大容量 ,这个我们在使用List的时候也经常会给集合一个初始化容量,只不过我们没有直接的方法获得这个初始化容量的值
  •     len 获取slice的长度
  •     append 添加一个或者多个元素,返回相同类型的切片
  •     copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数。(go中,函数是可以返回多个值的,哈哈,这个功能是不是感觉挺不错,如果java中也可以就好了)
    说到cap函数,提到了切片的容量,就要说一下切片的操作方式的三个参数的形式:
arr := [10]int{1,2,3,4,5,6,7,8,9,10}
slice3 := arr[:4:6]
    如上slice3的初始化的[]中多了一个参数,这个参数就相当于我们在java中使用集合给定一个初始容量一样,给这个切片定义一个初始容量。

    接下来就对比一下go的slice和java中的ArrayList:
  •   本质上都是数组,ArrayList使用数组来存储,slice操作的也是数组,并且都可以自动扩容;
  •   因为使用的是数组,所以他们也都是有序的,可重复的。
  •   slice是引用类型,所以作为参数传递的时候,效果和java中的List传参是一样的。
    在go的container容器包中还提供了list、heap、ring等,在以后我会专门的进行对比。


     三,Map
    map,也叫字典或集合,和java中的map是一样的,是一个键值对的集合,但在go的定义方式上和java就有一些区别了,先看一下java的初始化:
//java
Map<Object,Object> map = new HashMap<>(10);

    在java中所有对象的父类都是Object,所以这个集合的key和value可以是任意对象,但通常很少这么用,我们都会根据key和value的类型进行分别指定,这样在处理的过程中也方便使用,不用类型转换;

    再有java中的Map是接口,初始化的时候需要指定实现类,在go中,没有类,但是有结构体(struct),也可定义接口,也可实现集成,这个后面再说,先来看下go中map的定义:

//go
var map1 map[int]string //声明,没有初始化的时候是nil,此时是不能进行key操作的
map1 = map[int]string{} //初始化之后才能进行操作,初始化一个空的集合
map1 = map[int]string{0:"zero",1:"1"} //也可以在此添加初始数据
//map1 = make(map[int]string) //还可以使用make函数来初始化
map1[1] = "one"
map1[2] = "two"
fmt.Println("map1",map1)  //map1 map[0:zero 1:one 2:two]
delete(map1, 2)           //删除key
fmt.Println("map1",map1)  //map1 map[0:zero 1:one]
    map的定义需要使用map关键字,[]中的是key的类型,后面跟着的是value的类型。

    map在声明之后,初始化之前是不能进行键值对操作的,但是可以使用len、range等函数(后面会说到)进行操作,并不会报错,而如果是java,这个时候肯定会报错的,因为还是null。

    我们看到map添加数据就像使用数组一样,只不过[]中不是index,而是key,不用显式的get、add,很方便,不过在移除的时候需要用到delete方法。

    同样,我们对go原生的map和java中的HashMap(真的很像)进行对比:
  •     都不能通过index进行操作,只能通过key进行操作
  •     go中的map是无序的,类似java中的HashMap,也是无需的
  •     go的map不是基本类型,是线程不安全的,同样类似于HashMap。(java中有对应的线程安全实现HashTable)
  •     map是引用类型,所以在作为参数使用的时的效果依然是引用传参的效果,和java中的map传参效果相同。

    

 四,遍历

    接下来说一下内置的range函数,说了上边的数组、切片还有map后,当然就会想到怎么遍历,rang就是干这个用的,直接上例子:

//遍历数组
arr:= []int{1,2,3,4,5}
fmt.Println(arr)
for v:=range arr{
  fmt.Println(v)
}

//遍历slice
slice := arr[:3]
fmt.Println(slice)
for v:= range slice{
  fmt.Println(v)
}

//遍历map
var map1 map[int]string = make(map[int]string) //还可以使用make函数来初始化
map1[1] = "one"
map1[2] = "two"
fmt.Println("map1",map1)
for k,v := range map1{
  fmt.Println(k,"=",v)
}

    又看到了多个返回值,用起来真的好爽啊,哈哈。

    今天就说这么多吧,今天提到了slice和map是两个引用类型,还提到make函数,这些就留在下次说吧。


猜你喜欢

转载自blog.csdn.net/mingjia1987/article/details/80494406