Head First Go - 第六部分切片


前言

《Head First Go》第六章


1. 切片

切片实际上是一个Go的数据结构,我们可以增加更多值。和数组对比,切片是由多个相同类型的元素组成,但是切片可以追加元素。

声明切片: 声明切片不会自动创建一个切片,我们需要调用make函数。和数组对比,声明切片不需要显式定义这个切片大小

var mySlice []string	//声明一个切片,但是没有创建

创建切片: 使用make函数创建切片

mySlice = make([]string, 7)

声明出来之后,用法和数组一样,下标索引


短变量创建切片:

primes := make([]int, 7)

同样,对于数组来说,for…range和len函数也是可以用到切片上面的

package main

import "fmt"

func main() {
	primes := make([]int, 7)

	//或者使用_进行占位
	for index, i :=range primes{
		fmt.Print(index, i, " ");
	}
}



2. 切片字面量

下面是一行的:

notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"}		//切片字面量赋值

多行的:

primes :=[]int{
		1,
		2,
		3,		//记得加逗号
}



3. 切片运算符

为什么有了数组还需要切片呢?因为切片就是在数组的基础上建立的,切片仅仅是数组中的一部分(或者所有)的元素的视图。

当使用make函数或者切片字面量创建一个切片的时候,底层的数组会自动创建出来(只有通过切片,才可以访问到)。但是我们也可以自己创建一个数组,然后基于数组通过切片运算符创建一个切片。

func main() {
	notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"}		//切片字面量赋值
	fmt.Printf("notes: %v\n", notes)

	mySlice := notes[1:3]

	fmt.Printf("mySlice: %v\n", mySlice)	//mySlice: [re mi]
}

我们注意这种方法创建出来的切片只是包括了下标 1 到下标 2 的数据,也就是说 [i : j] ==> arr[i] … arr[j-1]

当然了我们也可以对 i 进行省略,比如上面的 notes[1:3] 可以使用 notes[ :3],结果就是从下标0到下标2

func main() {
	notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"}		//切片字面量赋值
	fmt.Printf("notes: %v\n", notes)

	mySlice := notes[:3]

	fmt.Printf("mySlice: %v\n", mySlice)	//mySlice: [do re mi]
}

我们可以省略 i,那么自然也可以省略 j,mySlice := notes[1:] 表示从下标1一直到最后,下面是代码:

package main

import "fmt"

func main() {
	notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"}		//切片字面量赋值
	fmt.Printf("notes: %v\n", notes)

	mySlice := notes[1:]

	fmt.Printf("mySlice: %v\n", mySlice)	//mySlice: [re mi fa so la ti]
}



4. 底层数组

切片其实就是底层数组的一个映射,切片并不会自己保存数据。那么当数组底层修改的时候,切面也会被修改。

package main

import "fmt"

func main() {
	notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"}		//切片字面量赋值
	fmt.Printf("notes: %v\n", notes)		//notes: [do re mi fa so la ti]

	slice1 := notes[0:3]
	fmt.Printf("slice1: %v\n", slice1)		//slice1: [do re mi]

	notes[0] = "修改"
	fmt.Printf("notes: %v\n", notes)		//notes: [修改 re mi fa so la ti]
	fmt.Printf("slice1: %v\n", slice1)		//slice1: [修改 re mi]
}

同样的,如果有多个切片,那么数组的修改会反映给多个所有的切片。在创建切片的时候我们是使用了make方法来创建的,因为这样我们就可以不用关心底层数组是怎么样的了。



5. 使用 “append” 函数在切片上添加数据

package main

import "fmt"

func main() {
	slice := []string{"a", "b"}
	fmt.Println(slice, len(slice))		//[a b] 2

	slice = append(slice, "c")	
	fmt.Println(slice, len(slice))		//[a b c] 3
	
	slice = append(slice, "d", "e")
	fmt.Println(slice, len(slice))		//[a b c d e] 5
}

有一点就是我们要确保把append返回值重新赋给传递给append的那个变量。这时为了避免append返回的切片中的一些不一致行为。

切片的底层数组并不能增长大小。如果数组没有足够的空间来保存新的元素,所有的元素就会被拷贝到一个新的更大的数组,并且切片会被更新为引用这个新的数组。也就是说如果我们有两个切片一开始引用了同一个数组,但是在append的过程中,由于到达了底层数组的容量,那么就会创建一个新的数组,可能导致这两个切片引用的数组不一样。

package main

import "fmt"

func main() {
	s1 := []string{"s1", "s1"}
	s2 := append(s1, "s2", "s2")
	s3 := append(s2, "s3", "s3")
	s4 := append(s3, "s4", "s4")
	//打印切片
	fmt.Println(s1, s2, s3, s4)		//[s1 s1] [s1 s1 s2 s2] [s1 s1 s2 s2 s3 s3] [s1 s1 s2 s2 s3 s3 s4 s4]
	s4[0] = "XX"
	fmt.Println(s1, s2, s3, s4)		//[s1 s1] [s1 s1 s2 s2] [XX s1 s2 s2 s3 s3] [XX s1 s2 s2 s3 s3 s4 s4]
	fmt.Printf("s1: %p, s2:%p, s3:%p, s4:%p", s1, s2, s3, s4)	//s1: 0xc00004e3c0, s2:0xc000024080, s3:0xc000110000, s4:0xc000110000
}

可以看到的是s1和s2的地址不一样,而s3和s4的地址一样,所以在添加的过程中,底层数组进行了重新创建的过程。导致了s3和s4的地址不一样了,那么正确的做法应该就是:将append的返回值赋值给s1



6. 切片和零值

没有赋值的切片:

package main

import "fmt"

func main() {
	floatSlice := make([]float64, 10)	//[0 0 0 0 0 0 0 0 0 0]
	boolSlice := make([]bool, 10)		//[false false false false false false false false false false]
	fmt.Println(floatSlice, boolSlice)
}

切片变量自己的 0 值:nil,一个没有赋值的切片变量为 nil

func main{
	var initSlice []int
	var stringSlice []string
	fmt.Printf("initSlice: %#v, stringSlice: %#v", initSlice, stringSlice)	//initSlice: []int(nil), stringSlice: []string(nil)
}

对于一个空的切片,使用len函数返回的结果是0,而使用append函数会追加值后返回新的切片,实际上这个切面是底层重新创建了的切片。

package main

import "fmt"

func main() {
	var slice []string
	fmt.Printf("旧的: %p\n", slice)		//旧的: 0x0
	if len(slice) == 0{
		slice = append(slice, "a")
	}
	fmt.Printf("slice: %v\n", slice)	//slice: [a]
	fmt.Printf("新的: %p\n", slice)		//新的: 0xc00004c250
}



7. 使用切片和 “append” 读取额外的文件行

我们使用切片来读取文件中的浮点数,这样就不需要提前设定好数值了。如果程序出错,我们返回 nil 和 error 即可。

package main

import (
	"bufio"
	"os"
	"strconv"
)

func GetFLoats(fileName string) ([]float64, error) {
	var numbers []float64
	file, err := os.Open(fileName)
	if err != nil{
		return nil, err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan(){
		number, err := strconv.ParseFloat(scanner.Text(), 64)	//读取文件
		if(err != nil){
			return nil, err
		}
		numbers = append(numbers, number)
	}
	err = file.Close();	//关闭文件
	if(err != nil){
		return nil, err;
	}
	if(scanner.Err() != nil){
		return numbers, scanner.Err();
	}
	return numbers, nil;
}

func main(){

}



8. 可变参数

用法:name...type, 用法和数组一样

package main

import (
	"fmt"
)

func manyArgs(numbers...int) int{
	var sum int
	for _, number := range numbers{
		sum += number
	}
	return sum
}

func main(){
	i := manyArgs(1, 2, 3)
	fmt.Printf("i: %v\n", i)	//i: 6
}

那么既然可变参数和切片都是多个同类型参数,能不能把切片作为参数传给可变参数呢?
在这里插入图片描述

可以看到传递切片会报错,那么有什么办法呢?Go语言提供了一个特殊的语法,只需要在可变参数后面加上...就可以了

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func manyArgs(numbers...int) int{
	var sum int
	for _, number := range numbers{
		sum += number
	}
	return sum
}

func main(){
	name := []int{1,2,3,4,5,6,7}
	i := manyArgs(name...)
	fmt.Printf("i: %v\n", i)	//i: 28

}





如有错误,欢迎指出!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Head First 设计模式(高清中文完整版带目录)+附书源码+HeadFirst设计模式学习伴侣.rar 又名: Head First Design Patterns 作者: (美)弗里曼(Freeman,E.) 副标题: Head First Design Patterns 简介 ······   你不想重新发明车轮(或者更差的是,没有充气车胎的轮子),所以你从设计模式中寻求帮助—— 设计模式是过去人们面对同样的软件设计问题所得到的经验。 有了设计模式,就可以利用其他人的实践经验的精华, 因此省下的时间可以用在其他的事情上,如:更有挑战性的事情、更复杂的事情以及更有趣的事情。你想要学习:    ·重要的模式 ;    ·何时使用某个模式,为何使用该模式;    ·如何在你自己的设计中马上采用这些模式;    ·何时不该使用该模式(如何避免滥用模式);    ·某个模式是依据哪些面向对象设计原理而设计出来的。    更重要的是,你想在学习设计模式的过程中,不感觉到昏昏欲睡。如果你曾经读过任何一本深入浅出(Head First)系列书籍, 你就会知道能够从本书中得到的是:透过丰富的视觉效果让你的大脑充分地运作。本书的编写运用许多最新的研究,包括神经生物学、认知科学以及学习理论, 这使得这本书能够将这些设计模式深深地烙印在你的脑海中,不容易被遗忘。你将会更擅长于解决软件设计中的问题, 并能够和你的团队成员用模式的语言来更好地沟通。 作者简介 ······   Eric Freeman以及Elisabeth Freeman是作家、教育家以及技术发明者。 他们在迪斯尼公司领导了四年的数字媒体以及因特网的开发,后来他们将这些经验应用在他们自己的媒体中, 包括这本书中。Eric具有耶鲁大学的计算机科学博士学位,Elisbeth具有耶鲁大学的计算机科学硕士学位。    Kathy Sierra(javaranch.com的创始者)以及Bert Bates是畅销的“深入浅出”(Head First)系列书籍的策划者, 也是Sun公司Java开发员认证考试的开发者。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值