GO基础篇 (四) 、数组,切片和map

一、数组

1.什么是数组

在Go语言中,数组是一种固定大小的数据结构,用于存储相同类型的元素。数组的大小在声明时就确定,并且不能更改。Go语言的数组声明方式如下:

var 数组名 [大小]元素类型

例如,以下是一个包含5个整数的数组的声明:

var myArray [5]int

在这个例子中,myArray 是一个包含5个整数的数组,每个元素的类型都是 int。数组的索引从0开始,所以可以通过 myArray[0]myArray[1]myArray[2] 等方式访问数组的元素。

数组的初始化可以使用以下方式:

myArray := [5]int{1, 2, 3, 4, 5}

或者,可以省略数组大小,让编译器根据初始化值的数量推断数组大小:

myArray := [...]int{1, 2, 3, 4, 5}

需要注意的是,由于数组是固定大小的,一旦声明后,其大小不能更改。如果你需要一个可以动态调整大小的集合,可以使用切片(slice)代替数组。

2.初始化数组

package main

import "fmt"

// 数组的赋值初始化
func main() {
	// 1.在定义数组的时候就直接初始化
	var arr1 = [5]int{1, 2, 3, 4, 5}
	fmt.Println(arr1)

	// 2.快速初始化 :=
	arr2 := [5]int{1, 2, 3, 4, 5}
	fmt.Println(arr2)

	// 3.数据如果来自用户,我不知道用户给我多少个数据
	// ... 代表数组的长度
	// Go的编译器会自动根据数组的长度来给 ... 赋值,自动推导长度
	// 注意点:这里的数组不是无限长的,也是固定的大小,大小取决于数组元素个数。
	var arr3 = [...]int{1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8}
	fmt.Println(len(arr3))
	fmt.Println(arr3)

	// 4.数组默认值,我只想给其中的某几个index位置赋值。
	// {index:值}
	var arr4 [10]int
	arr4 = [10]int{1: 100, 5: 500}
	fmt.Println(arr4) // [0 100 0 0 0 500 0 0 0 0]

}

3.数组元素遍历

package main

import "fmt"

/*
1、直接通过下标获取元素 arr[index]

2、 0-len i++ 可以使用for循环来结合数组下标进行遍历

3、for range:范围   (new)
*/
func main() {
   var arr1 = [5]int{1, 2, 3, 4, 5}
   // 错误:index 5 out of bounds [0:5] 数组下标越界
   // 数组的长度只有5,你要取出6个元素,不可能取出
   //fmt.Println(arr1[5])
   fmt.Println("------------------")
   // 获取数组的长度  len()
   // 下标从0开始,不能<=
   for i := 0; i < len(arr1); i++ {
      fmt.Println(arr1[i])
   }
   fmt.Println("------------------")
   // goland 快捷方式 数组.for,未来循环数组、切片很多时候都使用for    range
   // 就是将数组进行自动迭代。返回两个值 index、value
   // 注意点,如果只接收一个值,这个时候返回的是数组的下标
   // 注意点,如果只接收两个值,这个时候返回的是数组的下标和下标对应的值
   for _, value := range arr1 {
      fmt.Println(value)
   }
}

4.数组排序

arr := [6]int{1,2,3,4,5,0}
// 升序 ASC  : 从小到大  0,1,2,3,4,5   A-Z    00:00-24:00
// 降序 DESC : 从大到小  5,4,3,2,1,0

数组的排序,一组数是乱序的,我们如何将它按照升序或者降序排列。
排序算法:冒泡排序、插入排序、选择排序、希尔、堆、快排…

package main

import "fmt"

// 冒泡:每次筛选出一个最大或者最小的数.
/*
index   0   1   2   3   4
value   12  99  79  48  55
*/
// 冒泡排序逻辑,两两比较,大的往后移或者前移。 大
// 第一轮 : 12 79 48 55 99 // 5
// 第二轮 : 12 48 55 79 99 // 4
// 第三轮 : 12 48 55 79 99 // 3 //
// 第四轮 : 12 48 55 79 99 //
// 第五轮 : 12 48 55 79 99

// 代码实践
/*
   // 两个数判断,如果一个数大,则交换位置,大放到后面
   if arr[x] > arr[x+1] {
      arr[x], arr[x+1] = arr[x+1],arr[x]
   }
   // 多轮判断,for, 循环次数 【数组大小】
*/
func main() {
   arr := [...]int{12, 99, 79, 48, 55, 1, 110, 111, 23, 52, 354, 2, 3412, 3, 12, 31}
   fmt.Println("初始数组:", arr)
   // 冒泡排序
   // 1、多少轮
   for i := 1; i < len(arr); i++ {
      // 2、筛选出来最大数字以后,我们下次不需要将它再计算了
      for j := 0; j < len(arr)-i; j++ {
         // 比较 // 改变升降序只需要改变符号即可
         if arr[j] < arr[j+1] {
            arr[j], arr[j+1] = arr[j+1], arr[j]
         }
      }
   }
}

5.多维数组

package main
import "fmt"
func main() {
   // 定义一个多维数组  二维
   arr := [3][4]int{
      {0, 1, 2, 3},   // arr[0]  //数组
      {4, 5, 6, 7},   // arr[1]
      {8, 9, 10, 11}, // arr[2]
   }
   // 二维数组,一维数组存放的是一个数组
   fmt.Println(arr[0])
   // 要获取这个二维数组中的某个值,找到对应一维数组的坐标,arr[0] 当做一个整体
   fmt.Println(arr[0][1])
   fmt.Println("------------------")
   // 如何遍历二维数组
   for i := 0; i < len(arr); i++ {
      for j := 0; j < len(arr[i]); j++ {
         fmt.Println(arr[i][j])
      }
   }
   // for range
   for i, v := range arr {
      fmt.Println(i, v)
   }
}

二、切片

1.什么是切片

在Go语言中,切片(slice)是对数组一个片段的引用。切片不需要指定固定的大小,而是可以动态调整。切片的声明形式如下:

var 切片名 []元素类型

或者使用 make 函数来创建切片:

切片名 := make([]元素类型, 长度, 容量)

以下是一些关于切片的重要概念:

  1. 切片声明和初始化:

    // 使用数组创建切片
    myArray := [5]int{1, 2, 3, 4, 5}
    mySlice := myArray[1:4] // 创建一个包含 myArray[1] 到 myArray[3] 的切片
    
    // 使用 make 函数创建切片
    mySlice := make([]int, 3, 5) // 创建一个长度为3,容量为5的切片
    
  2. 切片的长度和容量:

    • 长度(len)是切片中元素的实际数量。
    • 容量(cap)是切片底层数组的大小。
  3. 切片的操作:

    • 添加元素:使用 append 函数可以在切片的末尾添加元素。
    mySlice = append(mySlice, 6)
    
    • 切片的切割:
    newSlice := mySlice[1:3]
    
  4. 切片是引用类型:
    修改切片会影响底层数组,因为切片本身不存储数据,而是引用底层数组的一部分。

  5. 空切片:
    空切片表示一个长度和容量都为0的切片,可以用 nil 表示。

var emptySlice []int // 空切片,等同于 nil

切片在Go语言中广泛用于动态数据集合的处理,相比数组更灵活,并且可以更方便地进行操作和管理内存。

2.切片遍历

package main

import "fmt"

func main() {
	s1 := make([]int, 0, 5)
	fmt.Println(s1)
	// 切片扩容,append()
	s1 = append(s1, 1, 2)
	fmt.Println(s1)
	// 问题:容量只有5个,那能放超过5个的吗? 可以,切片是会自动扩容的。
	s1 = append(s1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7)
	fmt.Println(s1)

	// 切片扩容之引入另一个切片。
	// new : 解构   slice.. ,解出这个切片中的所有元素。
	s2 := []int{100, 200, 300, 400}
	// slice = append(slice, anotherSlice...)
	// ... 可变参数 ...xxx
	// [...] 根据长度变化数组的大小定义
	// anotherSlice... , slice...解构,可以直接获取到slice中的所有元素
	// s2... = {100,200,300,400}
	s1 = append(s1, s2...)

	// 遍历切片
	for i := 0; i < len(s1); i++ {
		fmt.Println(s1[i])
	}

	for i := range s1 {
		fmt.Println(s1[i])
	}
}

3.使用数组创建切片

package main

import "fmt"

// 切片本身是不保存数据的,只是指向了底层数组。
func main() {
   // 数组 [0,10)   数组的截取 [start,end]
   // 数组
   arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0xc000018230
   // 通过数组来创建切片 [start,end)
   /*
     arr = 10个数
     1,  0xc000000001
     2,  0xc000000002
     3,  0xc000000003
     4,  0xc000000004
     5,  0xc000000005
   */
   s1 := arr[:5]  // 1-5   // 0xc000018230 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   s2 := arr[3:8] // 4-8   // 0xc000018248
   s3 := arr[5:]  //  6-10 // 0xc000018258
   s4 := arr[:]   //  0-10 // 0xc000018230

   fmt.Println(s1)
   fmt.Println(s2)
   fmt.Println(s3)
   fmt.Println(s4)
   // 查看容量和长度
   fmt.Printf("s1 len:%d, cap:%d\n", len(s1), cap(s1)) // s1 len:5, cap:10
   fmt.Printf("s2 len:%d, cap:%d\n", len(s2), cap(s2)) // s2 len:5, cap:7
   fmt.Printf("s3 len:%d, cap:%d\n", len(s3), cap(s3)) // s3 len:5, cap:5
   fmt.Printf("s4 len:%d, cap:%d\n", len(s4), cap(s4)) // s4 len:10, cap:10

   // 内存地址
   fmt.Printf("%p\n", s1) // 指向了原数组
   fmt.Printf("%p\n", s2) // 截断了后,指向了最新截断的元素的下标开始
   fmt.Printf("%p\n", s3)
   fmt.Printf("%p\n", &arr[5])
   fmt.Printf("%p\n", s4) // 指向了原数组
   fmt.Printf("%p\n", &arr)

   // 修改数组的内容, 切片也随之发生了变化 (切:切片不保存数据-->底层的数组 )
   arr[2] = 100
   fmt.Println(arr) // [1 2 100 4 5 6 7 8 9 10]
   fmt.Println(s1)  // [1 2 100 4 5]
   fmt.Println(s2)  // [4 5 6 7 8]
   fmt.Println(s3)  // [6 7 8 9 10]
   fmt.Println(s4)  // [1 2 100 4 5 6 7 8 9 10]
   fmt.Println("------------------------")
   // 修改切片的内容,发现数组也随之发生了变化。(本质:修改的都是底层的数组)
   s2[2] = 80
   fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
   fmt.Println(s1)  // [1 2 100 4 5]
   fmt.Println(s2)  // [4 5 80 7 8]
   fmt.Println(s3)  // [80 7 8 9 10]
   fmt.Println(s4)  // [1 2 100 4 5 80 7 8 9 10]
   fmt.Println("------------------------")
   // 切片扩容
   s1 = append(s1, 11, 12, 13, 14, 15, 16)
   fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
   // 如果在切片容量内加,会导致原来的数组数据发生修改。只有扩容之后,才会导致底层数组会产生拷贝。新的数组
   fmt.Println(s1) // [1 2 100 4 5 11 12 13 14 15 16]
   fmt.Println(s2) // [4 5 80 7 8]
   fmt.Println(s3) // [80 7 8 9 10]
   // 查看内存地址,已经不是同一个了
   fmt.Printf("%p\n", s1)   // 0xc0000d2000
   fmt.Printf("%p\n", &arr) // 0xc0000ac0f0

}

4.深浅拷贝

深拷贝:拷贝是数据的本身

  • 值类型的数据,默认都是深拷贝,array、int、float、string、bool、struct…

浅拷贝:拷贝是数据的地址,会导致多个变量指向同一块内存。

  • 引用类型的数据: slice、map
  • 因为切片是引用类的数据,直接拷贝的是这个地址

切片怎么实现深拷贝 copy

package main

import "fmt"

// 切片实现深拷贝
func main() {
   // 将原来切片中的数据拷贝到新切片中
   s1 := []int{1, 2, 3, 4}
   s2 := make([]int, 0) // len:0 cap:0
   for i := 0; i < len(s1); i++ {
      s2 = append(s2, s1[i])
   }
   fmt.Println(s1)
   fmt.Println(s2)
   s1[0] = 100
   fmt.Println(s1)
   fmt.Println(s2)

   // copy
   s3 := []int{5, 6, 7}
   fmt.Println(s2)
   fmt.Println(s3)

   // 将s3中的元素拷贝到s2中
   //copy(s2, s3)
   // 将s2中的元素拷贝到s3中
   copy(s3, s2)
   fmt.Println(s2)
   fmt.Println(s3)
}

三、map

1.什么是map

在Go语言中,map 是一种集合类型,用于存储键值对。map 提供了一种通过键快速查找值的机制。map 的声明和初始化形式如下:

// 使用 make 函数创建一个空的 map
myMap := make(map[键的类型]值的类型)

// 声明并初始化一个 map
myMap := map[键的类型]值的类型{1:1,2:2,
    // ...
}

以下是一些关于 map 的重要概念:

  1. 键值对:

    • map 存储的是键值对,其中每个键必须是唯一的。
    • 键和值的类型可以是任意类型,只要它们满足 == 操作符的要求。
  2. map 的初始化:

    • 使用 make 函数创建一个空的 map。
    • 使用字面量初始化 map。
// 示例:创建和初始化 map
ages := make(map[string]int)
ages["Alice"] = 25
ages["Bob"] = 30

// 或者使用字面量初始化
ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
    // ...
}
  1. 访问和修改 map:

    • 使用键来访问值:value := myMap[key]
    • 使用键来修改值:myMap[key] = newValue
  2. 删除元素:

    • 使用 delete 函数删除 map 中的元素:delete(myMap, key)
// 示例:删除 map 中的元素
delete(ages, "Alice")
  1. 判断键是否存在:
    • 使用多返回值的方式判断键是否存在,如果键存在,第二个返回值为 true,否则为 false
age, ok := ages["Alice"]
if ok {
    // 键存在,可以使用 age
} else {
    // 键不存在
}
  1. 迭代 map:
    • 使用 range 关键字可以迭代 map 中的键值对。
for key, value := range ages {
    fmt.Println(key, value)
}

map 是一个非常方便的数据结构,适用于需要通过键值对进行快速查找的场景。需要注意的是,map 是无序的,每次迭代得到的顺序可能不同。如果需要有序的键值对,可以使用切片来保存键,并排序切片。

2.map与切片结合使用

map结合slice使用

package main

import "fmt"

/*
需求:
1、创建map来存储人的信息,name,age,sex,addr
2、每个map保存一个的信息
3、将这些map存入到切片中
4、打印这些数据
*/
func main() {
   
   user1 := make(map[string]string)
   user1["name"] = "xiaoming"
   user1["age"] = "27"
   user1["sex"] = "男"
   user1["addr"] = "重庆"

   user2 := make(map[string]string)
   user2["name"] = "xiaohong"
   user2["age"] = "30"
   user2["sex"] = "男"
   user2["addr"] = "长沙"

   user3 := map[string]string{"name": "小蓝", "age": "18", "sex": "男", "addr": "火星"}
   fmt.Println(user3)

   // 3个数据有了,存放到切片中,供我们使用
   userDatas := make([]map[string]string, 0, 3)
   userDatas = append(userDatas, user1)
   userDatas = append(userDatas, user2)
   userDatas = append(userDatas, user3)

   fmt.Println(userDatas)

   // 0 map[string]string
   for _, user := range userDatas {
      //fmt.Println(i)
      fmt.Println(user["addr"])
   }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值