Go语言之容器总结

news2025/1/12 22:57:14

目录

1.值类型

1.1. 数组Array

数组遍历

数组初始化

值拷贝

内置函数len、cap

2. 引用数据类型

2.1. 切片slice

切片初始化

切片的内存布局

通过slice修改struct array值

用append内置函数操作切片(切片追加)

slice自动扩容

slice中cap重新分配规律

copy函数

字符串和切片(string and slice)

golang slice data[:6:8] 两个冒号的理解

2.2. Map

声明map

map初始化

map操作

map遍历

迭代时删除


1.值类型

1.1. 数组Array

Golang Array和以往认知的数组有很大不同。

1. 数组:是同一种数据类型的固定长度的序列。
2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
7.支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
8.指针数组 [n]*T,数组指针 *[n]T。

数组遍历

一维数组:

    for i := 0; i < len(a); i++ {
    }
    for index, v := range a {
    }

多维数组:

var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

for k1, v1 := range f {
	for k2, v2 := range v1 {
		fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
	}
	fmt.Println()
}

输出结果:
(0,0)=1 (0,1)=2 (0,2)=3 
(1,0)=7 (1,1)=8 (1,2)=9 

数组初始化

一维数组:

全局:
    var arr0 [5]int = [5]int{1, 2, 3}
    var arr1 = [5]int{1, 2, 3, 4, 5}
    var arr2 = [...]int{1, 2, 3, 4, 5, 6}
    var str = [5]string{3: "hello world", 4: "tom"}

局部:
    a := [3]int{1, 2}           // 未初始化元素值为 0。
	b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。
	c := [5]int{2: 100, 4: 200} // 使用引号初始化元素。
	d := [...]struct {
		name string
		age  uint8
	}{
		{"user1", 10}, // 可省略元素类型。
		{"user2", 20}, // 别忘了最后一行的逗号。
	}

多维数组:

全局
    var arr0 [5][3]int
    var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

局部:
    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
	b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

值拷贝

值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。

package main

import (
	"fmt"
)

func test(x [2]int) {
	fmt.Printf("x: %p\n", &x)
	x[1] = 1000
}

func main() {
	a := [2]int{}
	fmt.Printf("a: %p\n", &a)

	test(a)
	fmt.Println(a)
}

输出结果:

a: 0xc42007c010
x: 0xc42007c030
[0 0]

内置函数len、cap

内置函数 len 和 cap 都返回数组长度 (元素数量)。

package main

func main() {
	a := [2]int{}
	println(len(a), cap(a)) 
}

输出结果:

2 2

2. 引用数据类型

Golang的引用类型包括 slice、map 和 channel。它们有复杂的内部结构,除了申请内存外,还需要初始化相关属性。

内置函数 new 计算类型大小,为其分配零值内存,返回指针。而 make 会被编译器翻译 成具体的创建函数,由其分配内存和初始化成员结构,返回对象而非指针。

package main

func main() {
    a := []int{0, 0, 0} // 提供初始化表达式。
    a[1] = 10

    b := make([]int, 3) // make slice
    b[1] = 10

    c := new([]int)
    c[1] = 10 // ./main.go:11:3: invalid operation: c[1] (type *[]int does not support indexing)
}

引用类型:

  • 变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过GC回收。
  • 获取指针类型所指向的值,使用:" * " 取值符号 。比如:var *p int, 使用*p获取p指向的值
  • 指针、slice、map、chan等都是引用类型。

new和make的区别

make 用来创建map、slice、channel
new 用来创建值类型

new 和 make 均是用于分配内存:

  • new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。它也可以被用于基本类型:v := new(int)。
  • make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作。new() 是一个函数,不要忘记它的括号。

2.1. 切片slice

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
2. 切片的长度可以改变,因此,切片是一个可变的数组。
3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。 
4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int。
6. 如果 slice == nil,那么 len、cap 结果都等于 0。

切片初始化

切片初始化
全局:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end] 
var slice1 []int = arr[:end]        
var slice2 []int = arr[start:]        
var slice3 []int = arr[:] 
var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素

局部:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]        
slice7 := arr[start:]     
slice8 := arr[:]  
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素

通过make来创建切片

var slice []type = make([]type, len)
slice  := make([]type, len)
slice  := make([]type, len, cap)

切片的内存布局

读写操作实际目标是底层数组,只需注意索引号的差别。

package main

import (
	"fmt"
)

func main() {
	data := [...]int{0, 1, 2, 3, 4, 5}

	s := data[2:4]
	s[0] += 100
	s[1] += 200

	fmt.Println(s)
	fmt.Println(data)
}

输出:
[102 203]
[0 1 102 203 4 5]

可直接使用make创建 slice 对象,自动分配底层数组。

使用 make 动态创建 slice,避免了数组必须用常量做长度的麻烦。还可用指针直接访问底层数组,退化成普通数组操作。

package main

import "fmt"

func main() {
	s1 := []int{0, 1, 2, 3, 8: 100} // 通过初始化表达式构造,可使用索引号。
	fmt.Println(s1, len(s1), cap(s1))

	s2 := make([]int, 6, 8) // 使用 make 创建,指定 len 和 cap 值。
	fmt.Println(s2, len(s2), cap(s2))

	s3 := make([]int, 6) // 省略 cap,相当于 cap = len。
	fmt.Println(s3, len(s3), cap(s3))
}

输出结果:
[0 1 2 3 0 0 0 0 100] 9 9
[0 0 0 0 0 0] 6 8
[0 0 0 0 0 0] 6 6

通过slice修改struct array值

package main

import (
	"fmt"
)

func main() {
	d := [5]struct {
		x int
	}{}

	s := d[:]

	d[1].x = 10
	s[2].x = 20

	fmt.Println(d)
	fmt.Printf("%p, %p\n", &d, &d[0])
}

输出结果:
[{0} {10} {20} {0} {0}]
0xc4200160f0, 0xc4200160f0

用append内置函数操作切片(切片追加)

append :向 slice 尾部添加数据,返回新的 slice 对象。

package main

import (
	"fmt"
)

func main() {

	var a = []int{1, 2, 3}
	fmt.Printf("slice a : %v\n", a)
	var b = []int{4, 5, 6}
	fmt.Printf("slice b : %v\n", b)
	c := append(a, b...)
	fmt.Printf("slice c : %v\n", c)
	d := append(c, 7)
	fmt.Printf("slice d : %v\n", d)
	e := append(d, 8, 9, 10)
	fmt.Printf("slice e : %v\n", e)
}

输出:
slice a : [1 2 3]
slice b : [4 5 6]
slice c : [1 2 3 4 5 6]
slice d : [1 2 3 4 5 6 7]
slice e : [1 2 3 4 5 6 7 8 9 10]

slice自动扩容

超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。

package main

import (
	"fmt"
)

func main() {

	data := [...]int{0, 1, 2, 3, 4, 10: 0}
	s := data[:2:3]
    fmt.Printf("array s: %v, len:%v, cap:%v\n", s, len(s), cap(s)) // array s: [0 1], len:2, cap:3

	s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。

	fmt.Println(s, data)         // 重新分配底层数组,与原数组无关。
	fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。

}

输出:
array s: [0 1], len:2, cap:3
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc0000180c0 0xc000066060

slice中cap重新分配规律

package main

import (
	"fmt"
)

func main() {

	s := make([]int, 0, 1)
	c := cap(s)

	for i := 0; i < 50; i++ {
		s = append(s, i)
		if n := cap(s); n > c {
			fmt.Printf("cap: %d -> %d\n", c, n)
			c = n
		}
	}

}

输出:
cap: 1 -> 2
cap: 2 -> 4
cap: 4 -> 8
cap: 8 -> 16
cap: 16 -> 32
cap: 32 -> 64
  • 从输出结果可以看出,append 后的 s 重新分配了底层数组,并复制数据。如果只追加一个值,则不会超过 s.cap 限制,也就不会重新分配。
  • 通常以 2 倍容量重新分配底层数组。在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制开销。或初始化足够长的 len 属性,改用索引号进行操作。及时释放不再使用的 slice 对象,避免持有过期数组,造成 GC 无法回收。

copy函数

切片拷贝

s1 := []int{1, 2, 3, 4, 5}
fmt.Printf("slice s1 : %v\n", s1)
s2 := make([]int, 10)
fmt.Printf("slice s2 : %v\n", s2)
copy(s2, s1)   //func copy(dst, src []Type) int
fmt.Printf("copied slice s1 : %v\n", s1)
fmt.Printf("copied slice s2 : %v\n", s2)

输出:
slice s1 : [1 2 3 4 5]
slice s2 : [0 0 0 0 0 0 0 0 0 0]
copied slice s1 : [1 2 3 4 5]
copied slice s2 : [1 2 3 4 5 0 0 0 0 0]

字符串和切片(string and slice)

string底层就是一个byte的数组,因此,也可以进行切片操作。

package main

import (
	"fmt"
)

func main() {
	str := "hello world"
	s1 := str[0:5]
	fmt.Println(s1)

	s2 := str[6:]
	fmt.Println(s2)
}

输出:
hello
world

string本身是不可变的,因此要改变string中字符。需要如下操作:

英文字符串:

package main

import (
	"fmt"
)

func main() {
	str := "Hello world"
	s := []byte(str) //中文字符需要用[]rune(str)
	s[6] = 'G'
	s = s[:8]
	s = append(s, '!')
	str = string(s)
	fmt.Println(str)
}

输出:
Hello Go!

中文字符串:

package main

import (
	"fmt"
)

func main() {
	str := "你好,世界!hello world!"
	s := []rune(str) 
	s[3] = '够'
	s[4] = '浪'
	s[12] = 'g'
	s = s[:14]
	str = string(s)
	fmt.Println(str)
}

输出:
你好,够浪!hello go

golang slice data[:6:8] 两个冒号的理解

  • 常规slice , data[6:8],从第6位到第8位(返回6, 7),长度len为2, 最大可扩充长度cap为4(6-9)
  • 另一种写法: data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8
  • a[x:y:z] 切片内容 [x:y] 切片长度: y-x 切片容量:z-x
package main

import (
	"fmt"
)

func main() {
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	d1 := slice[6:8]
	fmt.Println(d1, len(d1), cap(d1))
	d2 := slice[:6:8]
	fmt.Println(d2, len(d2), cap(d2))
}

2.2. Map

Golang Map:引用类型,哈希表。一堆键值对的未排序集合。
键必须是支持相等运算符 ("=="、"!=") 类型, 如 number、string、 pointer、array、struct,以及对应的 interface。值可以是任意类型,没有限制。

声明map

var map变量名 map[key] value
其中:key为键类型,value为值类型
例如:value不仅可以是标注数据类型,也可以是自定义数据类型
package main

type personInfo struct {
	ID      string
	Name    string
	Address string
}

var m1 map[string]int
var m2 map[string]personInfo

func main() {}

map初始化

直接初始化(创建)

package main

import (
	"fmt"
)

var m1 map[string]float32 = map[string]float32{"C": 5, "Go": 4.5, "Python": 4.5, "C++": 2}

func main() {

	m2 := map[string]float32{"C": 5, "Go": 4.5, "Python": 4.5, "C++": 2}
	m3 := map[int]struct {
		name string
		age  int
	}{
		1: {"user1", 10}, // 可省略元素类型。
		2: {"user2", 20},
	}
	fmt.Printf("全局变量 map m1 : %v\n", m1)
	fmt.Printf("局部变量 map m2 : %v\n", m2)
	fmt.Printf("局部变量 map m3 : %v\n", m3)
}

输出结果:
全局变量 map m1 : map[Python:4.5 C++:2 C:5 Go:4.5]
局部变量 map m2 : map[C++:2 C:5 Go:4.5 Python:4.5]
局部变量 map m3 : map[2:{user2 20} 1:{user1 10}]

注意:由m1,m2可以看出map是键值对的无序集合

通过make初始化(创建)

  • Go语言提供的内置函数make()可以用于灵活地创建map。
  • 预先给 make 函数一个合理元素数量参数,有助于提升性能。因为事先申请一大块内存,可避免后续操作时频繁扩张。
package main

import (
	"fmt"
)

func main() {
	// 创建了一个键类型为string,值类型为int的map
	m1 := make(map[string]int)
	// 也可以选择是否在创建时指定该map的初始存储能力,如创建了一个初始存储能力为5的map
	m2 := make(map[string]int, 5)

	m1["a"] = 1
	m2["b"] = 2
	fmt.Printf("局部变量 map m1 : %v\n", m1)
	fmt.Printf("局部变量 map m2 : %v\n", m2)
}

输出:
局部变量 map m1 : map[a:1]
局部变量 map m2 : map[b:2]

map操作

插入、更新、查找、删除、判断是否存在、求长度

m := map[string]string{"key0": "value0", "key1": "value1"}
fmt.Printf("map m : %v\n", m)

//map插入
m["key2"] = "value2"
fmt.Printf("inserted map m : %v\n", m)
	
//map修改
m["key0"] = "hello world!"
fmt.Printf("updated map m : %v\n", m)
	
//map查找
val, ok := m["key0"]
if ok {
	fmt.Printf("map's key0 is %v\n", val)
}

// 长度:获取键值对数量。
len := len(m)
fmt.Printf("map's len is %v\n", len)

// cap 无效,error
// cap := cap(m)    //invalid argument m (type map[string]string) for cap
// fmt.Printf("map's cap is %v\n", cap)

// 判断 key 是否存在。
if val, ok = m["key"]; !ok {
	fmt.Println("map's key is not existence")
}

// 删除,如果 key 不存在,不会出错。
if val, ok = m["key1"]; ok {
	delete(m, "key1")
	fmt.Printf("deleted key1 map m : %v\n", m)
}

map遍历

不能保证迭代返回次序,通常是随机结果,具体和版本实现有关。

for k, v := range map {}
for _, v := range map {}
for k := range map {}

容器和结构体(map and struct)

语法比较:
map[type]struct
map[type]*struct

package main

import "fmt"

func main() {
	type user struct{ name string }
	/*
	   当 map 因扩张而重新哈希时,各键值项存储位置都会发生改变。
	   因此,map 被设计成 not addressable。
	   类似 m[1].name 这种期望透过原 value 指针修改成员的行为自然会被禁 。
	*/
	m := map[int]user{ //

		1: {"user1"},
	}
	// m[1].name = "Tom"
	// ./main.go:16:12: cannot assign to struct field m[1].name in map
	fmt.Println(m)

	// 正确做法是完整替换 value 或使用指针。
	u := m[1]
	u.name = "Tom"
	m[1] = u // 替换 value。

	m2 := map[int]*user{
		1: &user{"user1"},
	}

	m2[1].name = "Jack" // 返回的是指针复制品。透过指针修改原对象是允许的。
	fmt.Println(m2)
}

输出:
map[1:{user1}]
map[1:0xc42000e1e0]

迭代时删除

可以在迭代时安全删除键值。但如果期间有新增操作,那么就不知道会有什么意外了。

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		m := map[int]string{
			0: "a", 1: "a", 2: "a", 3: "a", 4: "a",
			5: "a", 6: "a", 7: "a", 8: "a", 9: "a",
		}

		for k := range m {
			m[k+k] = "x"
			delete(m, k)
		}

		fmt.Println(m)
	}
}

输出

//每次输出都会变化

map[36:x 28:x 32:x 2:x 8:x 10:x 12:x]
map[12:x 6:x 16:x 28:x 4:x 10:x 72:x]
map[12:x 14:x 16:x 18:x 20:x]
map[18:x 10:x 14:x 4:x 6:x 16:x 24:x]
map[12:x 16:x 4:x 40:x 14:x 18:x]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于yolov5算法的安全帽头盔检测源码+模型,Pytorch开发,智能工地安全领域中头盔目标检测的应用

基于yolov5算法的安全帽头盔检测|Pytorch开发源码模型 本期给大家打开的是YOLOv5在智能工地安全领域中头盔目标检测的应用。 完整代码下载地址&#xff1a;基于yolov5算法的安全帽头盔检测源码模型 可视化界面演示&#xff1a; &#x1f4a5;&#x1f4a5;&#x1f4a5;新增…

opencv c++ Mat CUDA的编译与使用

Mat 构造函数 cv::Mat img ; //默认 定义了一个Mat img cv::imread("image.jpg");//除了直接读取&#xff0c;还有通过大小构造等cv::Mat img cv::imread("image.png", IMREAD_GRAYSCALE); cv::Mat img_novel img;转换 Mat::convertTo(Mat& m, in…

【自学Java】Java方法

Java方法 Java方法教程 在 Java 语言 中&#xff0c;方法就是一段可重复调用的代码段。在平时开发直接交流中&#xff0c;也有一些同学喜欢把方法叫做函数&#xff0c;这两个其实是一个概念。 Java语言方法详解 语法 public void fun(Object param1,...){//do something }…

多线程与高并发(四)

【Exchanger】&#xff1a; package Ten_Class.t04.no139;import java.util.concurrent.Exchanger;public class T12_TestExchanger {static Exchanger<String> exchanger new Exchanger<>();public static void main(String[] args) {new Thread(() -> {Stri…

实验二十四 策略路由配置

实验二十四 策略路由配置实验要求&#xff1a; 某企业通过路由器AR1连接互联网&#xff0c;由于业务儒要&#xff0c;与两家运营商ISPA和ISPB相连。 企业网内的数据流从业务类型上可以分为两类&#xff0c; 一类来自于网络172.16.0.0/16&#xff0c;另 一类 来自于网络172.17.0…

百趣代谢组学分享:黑木耳多糖对小鼠肠道微生物及代谢表型的影响

文章标题&#xff1a;Effects of Auricularia auricula Polysaccharides on Gut Microbiota and Metabolic Phenotype in Mice 发表期刊&#xff1a;Foods 影响因子&#xff1a;5.561 作者单位&#xff1a;西北大学 百趣提供服务&#xff1a;发现代谢组学Standard-亲水版、1…

dataCompare大数据对比之异源数据对比

在从0到1介绍一下开源大数据比对平台dataCompare 已经详细介绍了dataCompare 的功能&#xff0c;目前dataCompare 已经实现同源数据的对比 一、dataCompare 现有核心功能如下&#xff1a; (1)数量级对比 (2)一致性对比 (3)差异case 自动发现 (4)定时调度自动对比数据 二、…

【个人解答版】笔试题-2023禾赛-FPGA

题目背景 笔试时间&#xff1a;2022.06.22应聘岗位&#xff1a;FPGA开发工程师 题目评价 难易程度&#xff1a;★★☆☆☆知识覆盖&#xff1a;★☆☆☆☆超纲范围&#xff1a;☆☆☆☆☆值得一刷&#xff1a;★☆☆☆☆ 文章目录1. 使用最少的电路实现二分频&#xff0c;给出…

《机器学习实战》chap1 机器学习概览

《机器学习实战》chap1 机器学习概览 Chap1 The Machine Learning Landscape 这本书第三版也已经出版了:https://github.com/ageron/handson-ml3 Hands-on Machine Learning with Scikit-Learn,Keras & TensorFlow 引入 很早的应用&#xff1a;光学字符识别(OCR&#xff0…

远程办公之怎样在外网登录在线答题网站

很多学校或企业因为教学、测试需要&#xff0c;为学生或员工提供了在线答题平台网站&#xff0c;但弊端是这种在线答题平台只能在校内或在企业内网访问使用&#xff0c;在外网是无法登录访问的。在无公网Ip服务器上部署的web&#xff0c;默认情况下只能内网访问&#xff0c;公网…

TLE4943C/CH505C轮速传感器芯片的输出协议介绍

Infineon公司的TLE4943是一款集成式有源磁场传感器&#xff0c;适用于基于霍尔技术的车轮速度应用。它的基本功能是测量磁极轮或铁磁齿轮的速度。它具有使用AK协议进行通信的两线电流接口。该协议除了提供速度信号外&#xff0c;还提供其他信息&#xff0c;如车轮旋转方向和气隙…

java安装教程-windows

检查是否已经安装过jav打开cmd命令窗口 输入 java -v下载java安装包网址&#xff1a;https://www.oracle.com/java/technologies/downloads/安装java双击运行程序jdk-19_windows-x64_bin.exe&#xff0c;点击下一步进行安装可以更改安装路径&#xff0c;注意安装路径不能有中文…

【TypeScript】TS类型守卫(六)

&#x1f431;个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️作者简介&#xff1a;前端领域新星创作者、华为云享专家、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff…

独立开发变现周刊(第86期):月收入4000美元的日程规划器

分享独立开发、产品变现相关内容&#xff0c;每周五发布。目录1、NotionReads: 在Notion中管理你的阅读书籍2、Zaap.ai: 面向创作者的一站式工具3、microfeed: 开源的可自我托管的轻量级内容管理系统(CMS)4、Reactive Resume&#xff1a;一个免费的开源简历生成器5、一个月收入…

2019年1月政企终端安全态势分析报告

声明 本文是学习2019年1月政企终端安全态势分析报告. 下载地址 http://github5.com/view/55037而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 漏洞利用病毒攻击政企分析 奇安信终端安全实验室监测数据显示&#xff0c;2019年4月&#xff0c;有6.7%的…

JavaScript中的元编程

紧接上回&#xff0c;伴随着Reflect&#xff0c;Proxy降世&#xff0c;为js带来了更便捷的元编程&#xff01; 什么是元编程&#xff1f;这词第一次听&#xff0c;有点懵&#xff0c;好像有点高级&#xff0c;这不得学一下装…进自己的知识库 概念 元编程是一种编程技术&…

【数据结构与算法】Collection接口迭代器

Java合集框架 数据结构是以某种形式将数据组织在一起的合集&#xff08;collection&#xff09;。数据结构不仅存储数据&#xff0c;还支持访问和处理数据的操作 在面向对象的思想里&#xff0c;一种数据结构也被认为是一个容器&#xff08;container&#xff09;或者容器对象…

【MySQL】MySQL表的七大约束

序号系列文章1【MySQL】MySQL介绍及安装2【MySQL】MySQL基本操作详解3【MySQL】MySQL基本数据类型4【MySQL】MySQL表的七大约束文章目录MySQL表的约束1&#xff0c;默认约束2&#xff0c;非空约束3&#xff0c;唯一约束4&#xff0c;主键约束5&#xff0c;自增约束6&#xff0c…

详细分析单调栈,及正确性证明

什么是单调栈 对于一个数组&#xff0c;需要对每个位置生成&#xff0c;左右两边离它最近的&#xff0c;比它小&#xff08;或比它大&#xff09;的位置在哪 例如&#xff1a; 如果对每个位置都遍历下左右两边&#xff0c;找到第一个比它小的位置&#xff0c;就是O(N ^ 2)的…

IPv6 时代如何防御 DDoS 攻击?

在互联网世界&#xff0c;每台联网的设备都被分配了一个用于标识和位置定义的 IP 地址。20 世纪 90 年代以来互联网的快速发展&#xff0c;联网设备所需的地址远远多于可用 IPv4 地址的数量&#xff0c;导致了 IPv4 地址耗尽。因此&#xff0c;协议 IPv6 的开发和部署已经刻不容…