Go 切片常用操作与使用技巧

1.什么是切片

在 Go 语言中的切片(slice)是一种灵活的动态数组,它可以自动扩展和收缩,是 Go 语言中非常重要的数据结构之一。切片是基于数组实现的,它的底层是数组,可以理解为对底层数组的抽象。它会生成一个指向数组的指针,并通过切片长度关联到底层数组部分或者全部元素。

2.切片底层结构

在 Go 语言的源码包中,src/runtime/slice.go 定义了切片的数据结构:

type slice struct {array unsafe.Pointerlen   intcap   int
}

切片占用 24 个字节:

  • array:指向底层数组的指针,占用 8 个字节。当你创建一个切片时,Go 会分配一个底层数组,并将切片的指针指向该数组。

  • len:切片中元素的数量,占用 8 个字节。

  • cap:切片的容量,cap 总是大于等于 len,占用 8 个字节。

3.初始化方式

通常用以下三种初始化切片:

  • 通过下标的方式获得数组的一部分;

slice := arr[1:3]
  • 使用字面量初始化新的切片;

slice := []int{1, 2, 3}
  • 使用关键字 make 创建切片:

slice := make([]int, 10)
//或
slice := make([]int, 3, 5)

make函数有三个参数:

  1. 第一个参数为切片类型,可以是[]int,[]string,[]float32等。
  2. 第二个参数为切片初始长度。
  3. 第三个为切片容量,该参数为可选参数。

注意点:

var slice1 []int 这是声明,此时切片为nil

例如:

package mainimport "fmt"func main() {var slice1 []intif slice1 == nil {fmt.Println("slice is nil!")}// slice1[0] = 9 报错panic: runtime error: index out of range [0] with length 0slice1 = append(slice1, 2) //可以fmt.Println(slice1)
}

结果:

slice is nil!
[2]

4.追加元素

append函数是Go语言内置的一个函数,用于向切片中追加元素。

s := []int{1, 2, 3}
s = append(s, 4)
fmt.Println(s) // 输出:[1 2 3 4]

//如果你要追加的元素是另一个切片,那么可以使用...运算符,这样可以把那个切片的所有元素都追加进来。
s := []int{1, 2, 3}
t := []int{4, 5, 6}
s = append(s, t...)
fmt.Println(s) // 输出:[1 2 3 4 5 6]

5.切片表达式

Golang 中通常的 slice 语法是 a[low:high],您可能很熟悉。还有另一种切片语法,形式为 a[low:high:max],它采用三个索引而不是两个索引。第三索引 max 是做什么的?

提示: 不是 Python 切片语法 a[low:high:step] 中的 step 步长索引。

答: 第三个索引用于设置切片的容量!在 Golang 规范中称为 “全切片表达式”。

简单切片表达式的格式[low:high]

如下面例子,n为一个切片,当用这个表达式[1:4]表示的是左闭右开[low, high)区间截取一个新的切片(例子中结果是[2 3 4]),切片被截取之后,截取的长度是high-low

n := []int{1, 2, 3, 4, 5, 6}
fmt.Println(n[1:4]) // [2 3 4]

切片表达式的开始low和结束索引high是可选的;它们分别默认为零和切片的长度:

n := []int{1, 2, 3, 4, 5, 6}
fmt.Println(n[:4]) // [1 2 3 4],:前面没有值,默认表示0
fmt.Println(n[1:]) // [2 3 4 5 6],:后面没有值,默认表示切片的长度
边界问题
  • 1、当n为数组或字符串表达式n[low:high]中low和high的取值关系:

0 <= low <=high <= len(n)

  • 2、当n为切片的时候,表达式n[low:high]中high最大值变成了cap(n),low和high的取值关系:

0 <= low <=high <= cap(n)

不满足以上条件会发送越界panic。

全切片表达式

n[low:high:max]

max表示新生成切片的容量,新切片容量等于max-low,表达式中low、high、max关系:

0 <= low <= high <= max <= cap(n)

继续刚才的例子,当计算n[1:4]的容量,用cap得到值等于5,用扩展表达式n[1:4:5],用cap重新计算得到新的容量值(5-1)等于4:

 fmt.Println(cap(n[1:4])) // 5fmt.Println(cap(n[1:4:5])) // 4
关于容量

n[1:4]的长度是3好理解(4-1),容量为什么是5?

因为切片n[1:4]和切片n是共享底层空间,所以它的容量并不等于他的长度3,根据1等于索引1的位置(等于值2),从值2这个元素开始到末尾元素6,共5个,所以n[1:4]容量是5。

如果append超过切片的长度会重新生产一个全新的切片,不会覆盖原来的:

func main() {n := []int{1, 2, 3, 4, 5, 6}n2 := n[1:4:5]         // 长度等于3,容量等于4fmt.Printf("%p\n", n2) // 0xc0000220c8n2 = append(n2, 5)fmt.Printf("%p\n", n2) // 0xc0000220c8n2 = append(n2, 6)fmt.Printf("%p\n", n2) // 地址发生改变,0xc00001e080
}

6.常见使用陷阱

切片作为参数传递陷阱

  • 在函数里修改切片元素的值,原切片的值也会被改变

  • 在函数里通过 append 方法,对切片执行追加元素的操作,可能会引起切片扩容,导致内存分配的问题,可能会对程序的性能 造成影响

  • 在函数里通过 append 函数,对切片执行追加元素的操作,原切片里不存在新元素

看一下代码示例:

func main() {s := []int{0, 2, 3}fmt.Printf("main中1 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 3 3 [0, 2, 3]sliceOperation(s)fmt.Printf("main中2 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 3 3 [1, 2, 3]
}func sliceOperation(s []int) {s[0] = 1fmt.Printf("sliceOperation中 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 3 3 [1, 2, 3]
}

结果:

main中1 切片的长度:3, 切片的容量:3, 切片的元素:[0 2 3]
sliceOperation中 切片的长度:3, 切片的容量:3, 切片的元素:[1 2 3]
main中2 切片的长度:3, 切片的容量:3, 切片的元素:[1 2 3]

看到切片中的元素会被改变,就认为改变切片中的元素数据就直接将切片作为参数传过去就可以了,但是如果用了append追加元素,就会发现追加的元素并不会加到原切片中:

func main() {s := []int{0, 2, 3}fmt.Printf("main中1 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 3 3 [0, 2, 3]sliceOperation(s)fmt.Printf("main中2 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 3 3 [1, 2, 3]
}func sliceOperation(s []int) {s = append(s, 4)fmt.Printf("sliceOperation中 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) // 4 6 [0, 2, 3]
}

结果:

main中1 切片的长度:3, 切片的容量:3, 切片的元素:[0 2 3]
sliceOperation中 切片的长度:4, 切片的容量:6, 切片的元素:[0 2 3 4]
main中2 切片的长度:3, 切片的容量:3, 切片的元素:[0 2 3]
  • 若想实现执行 append 函数之后,原切片也能得到新元素;需将函数的参数类型由 切片类型 改成 切片指针类型

func main() {s := []int{0, 2, 3}fmt.Printf("main中1 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) sliceOperation(&s)fmt.Printf("main中2 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(s), cap(s), s) 
}func sliceOperation(s *[]int) {*s = append(*s, 4)fmt.Printf("sliceOperation中 切片的长度:%d, 切片的容量:%d, 切片的元素:%v\n", len(*s), cap(*s), s) 
}

结果:

main中1 切片的长度:3, 切片的容量:3, 切片的元素:[0 2 3]
sliceOperation中 切片的长度:4, 切片的容量:6, 切片的元素:[0 2 3 4]
main中2 切片的长度:4, 切片的容量:6, 切片的元素:[0 2 3 4]

slice 通过 make 函数初始化,后续操作不当所造成的陷阱

1.使用 make 函数初始化切片后,如果在后续操作中没有正确处理切片长度,容易造成以下陷阱:

func main() {s := make([]int, 0, 4)s[0] = 1 // panic: runtime error: index out of range [0] with length 0
}

通过 make([]int, 0, 4) 初始化切片,虽说容量为 4,但是长度为 0,如果通过索引去赋值,会发生panic;为避免 panic,可以通过 s := make([]int, 4) 或 s := make([]int, 4, 4) 对切片进行初始化。

2.切片初始化不当,通过 append 函数追加新元素的位置可能于预料之外

func main() {s := make([]int, 4)s = append(s, 1)fmt.Println(s[0]) // 0s2 := make([]int, 0, 4)s2 = append(s2, 1)fmt.Println(s2[0]) // 1
}
  • 通过打印结果可知,对于切片 s,元素 1 没有被放置在第一个位置,而对于切片 s2,元素 1 被放置在切片的第一个位置。这是因为通过 make([]int, 4) 和 make([]int, 0, 4) 初始化切片,底层所指向的数组的值是不一样的:

    • 第一种初始化的方式,切片的长度和容量都为 4,底层所指向的数组长度也是 4,数组的值为 [0, 0, 0, 0],每个位置的元素被赋值为零值s = append(s, 1) 执行后,s 切片的值为 [0, 0, 0, 0, 1]

    • 第二种初始化的方式,切片的长度为 0,容量为 4,底层所指向的数组长度为 0,数组的值为 []s2 = append(s2, 1) 执行后,s2 切片的值为 [1]

    • 通过 append 向切片追加元素,会执行尾插操作。如果我们需要初始化一个空切片,然后从第一个位置开始插入元素,需要避免 make([]int, 4) 这种初始化的方式,否则添加的结果会在预料之外。

  • 性能陷阱

  • 内存泄露

    内存泄露是指程序分配内存后不再使用该内存,但未将其释放,导致内存资源被浪费。

    切片引用切片场景:如果一个切片有大量的元素,而它只有少部分元素被引用,其他元素存在于内存中,但是没有被使用,则会造成内存泄露。代码示例如下:

    var s []intfunc main() {sliceOperation()fmt.Println(s)}func sliceOperation() {a := make([]int, 0, 10)a = append(a, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)s = a[0:4]}

    上述代码中,切片 a 的元素有 10 个,而切片 s 是基于 a 创建的,它底层所指向的数组与 a 所指向的数组是同一个,只不过范围为前四个元素,而后六个元素依然存在于内存中,却没有被使用,这样会造成内存泄露。为了避免内存泄露,我们可以对代码进行改造:s = a[0:4] → s = append(s, a[0:4]...),通过 append 进行元素追加,这样切片 a 底层的数组没有被引用,后面会被 gc

7.切片技巧

Go在Github的官方Wiki上介绍了切片的技巧SliceTricks[4],另外这个项目Go Slice Tricks Cheat Sheet[5]基于SliceTricks做了一系列的图,比较直观。

AppendVector

这个是添加一个切片的操作,上面我们在切片操作中已经介绍过。

a = append(a, b...)

图片

Copy

这边给我们展示了三种copy的写法:

b := make([]T, len(a))
copy(b, a)// 效率一般比上面的写法慢,但是如果有更多,他们效率更好
b = append([]T(nil), a...)
b = append(a[:0:0], a...)// 这个实现等价于make+copy。
// 但在Go工具链v1.16上实际上会比较慢。
b = append(make([]T, 0, len(a)), a...)

图片

Cut

截掉切片[i,j)之间的元素:

a = append(a[:i], a[j:]...)

图片

Cut(GC)

上面的Cut如果元素是指针的话,会存在内存泄露,所以我们要对删除的元素设置nil,等待GC。

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

图片

Delete

删除索引位置i的元素:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

图片

Delete(GC)

删除索引位置i的元素:

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]

图片

Delete without preserving order

删除索引位置i的元素,把最后一位放到索引位置i上,然后把最后一位元素删除。这种方式底层并没有发生复制操作。

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

图片

Delete without preserving order(GC)

上面的删除操作,元素是一个指针的类型或结构体指针字段,会存在最后一个元素不能被GC掉,造成泄露,把末尾的元素设置nil,等待GC。

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]

图片

Expand

这个本质上是多个append的组合操作。

a = append(a[:i], append(make([]T, j), a[i:]...)...)

图片

Extend

用新列表扩展原来的列表

a = append(a, make([]T, j)...)

图片

Filter (in place)

下面代码演示原地删除Go切片元素:

n := 0
for _, x := range a {if keep(x) {a[n] = xn++}
}
a = a[:n]

图片

Insert

a = append(a[:i], append([]T{x}, a[i:]...)...)

第二个append会产生新的切片,产生一次copy,可以用以下代码方式,可免去第二次的copy:

s = append(s, 0 /* 先添加一个0值*/)
copy(s[i+1:], s[i:])
s[i] = x

图片

InsertVector

下面代码演示插入向量(封装了动态大小数组的顺序容器)的实现:

a = append(a[:i], append(b, a[i:]...)...)
func Insert(s []int, k int, vs ...int) []int {if n := len(s) + len(vs); n <= cap(s) {s2 := s[:n]copy(s2[k+len(vs):], s[k:])copy(s2[k:], vs)return s2}s2 := make([]int, len(s) + len(vs))copy(s2, s[:k])copy(s2[k:], vs)copy(s2[k+len(vs):], s[k:])return s2
}a = Insert(a, i, b...)

图片

Push

a = append(a, x)

图片

Pop

 x, a = a[len(a)-1], a[:len(a)-1]

图片

Push Front/Unshift

a = append([]T{x}, a...)

图片

Pop Front/Shift

x, a = a[0], a[1:]

图片

7 切片额外技巧

Filtering without allocating

下面例子演示数据过滤的时候,b基于原来的a存储空间来操作,并没有重新生成新的存储空间。

b := a[:0]
for _, x := range a {if f(x) {b = append(b, x)}
}

为了让截取之后没有使用的存储被GC掉,需要设置成nil:

for i := len(b); i < len(a); i++ {a[i] = nil // or the zero value of T
}

图片

Reversing

反转操作演示:

for i := len(a)/2-1; i >= 0; i-- {opp := len(a)-1-ia[i], a[opp] = a[opp], a[i]
}

还有一种方法:

for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {a[left], a[right] = a[right], a[left]
}

图片

Shuffling

洗牌算法。算法思想就是从原始数组中随机抽取一个新的数字到新数组中。

for i := len(a) - 1; i > 0; i-- {j := rand.Intn(i + 1)a[i], a[j] = a[j], a[i]
}

go1.10之后有内置函数Shuffle[6]

图片

Batching with minimal allocation

做批处理大的切片的时候,这个技巧可以了解下:

actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
batchSize := 3
batches := make([][]int, 0, (len(actions) + batchSize - 1) / batchSize)for batchSize < len(actions) {actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize])
}
batches = append(batches, actions)// 结果:
// [[0 1 2] [3 4 5] [6 7 8] [9]]

图片

In-place deduplicate (comparable)

删除有序数组中的重复项:

import "sort"in := []int{3,2,1,4,3,2,1,4,1} // any item can be sorted
sort.Ints(in)
j := 0
for i := 1; i < len(in); i++ {if in[j] == in[i] {continue}j++// preserve the original data// in[i], in[j] = in[j], in[i]// only set what is requiredin[j] = in[i]
}
result := in[:j+1]
fmt.Println(result) // [1 2 3 4]

图片

Move to front, or prepend if not present, in place if possible.

下面代码演示移动指定元素到头部:

// moveToFront moves needle to the front of haystack, in place if possible.
func moveToFront(needle string, haystack []string) []string {if len(haystack) != 0 && haystack[0] == needle {return haystack}prev := needlefor i, elem := range haystack {switch {case i == 0:haystack[0] = needleprev = elemcase elem == needle:haystack[i] = prevreturn haystackdefault:haystack[i] = prevprev = elem}}return append(haystack, prev)
}haystack := []string{"a", "b", "c", "d", "e"} // [a b c d e]
haystack = moveToFront("c", haystack)         // [c a b d e]
haystack = moveToFront("f", haystack)         // [f c a b d e]

图片

图片

Sliding Window

下面实现根据size的滑动窗口输出:

func slidingWindow(size int, input []int) [][]int {// returns the input slice as the first elementif len(input) <= size {return [][]int{input}}// allocate slice at the precise size we needr := make([][]int, 0, len(input)-size+1)for i, j := 0, size; j <= len(input); i, j = i+1, j+1 {r = append(r, input[i:j])}return r
}func TestSlidingWindow(t *testing.T) {result := slidingWindow(2, []int{1, 2, 3, 4, 5})fmt.Println(result) // [[1 2] [2 3] [3 4] [4 5]]
}

图片

最后的技巧章节参考微信公众号太白技术: Go切片与技巧(附图解)

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

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

相关文章

必应崩了?

目录 今天使用必应发现出现了不能搜索&#xff0c;弹出乱码的情况。 搜了一下&#xff0c;发现其他人也出现了同样的问题。 使用Edge浏览器的话&#xff0c;可以试着改一下DNS&#xff0c;有可能会恢复正常&#xff08;等官方修复了记得改回来&#xff09; 使用谷歌浏览器打开…

桂林电子科技大学计算机工程学院、广西北部湾大学计信学院莅临泰迪智能科技参观交流

5月18日&#xff0c;桂林电子科技大学计算机工程学院副院长刘利民、副书记杨美娜、毕业班辅导员黄秀娟、广西北部湾大学计信学院院长助理刘秀平莅临广东泰迪智能科技股份有限公司产教融合实训基地参观交流。泰迪智能科技副总经理施兴、广西分公司郑廷和、梁霜、培训业务部孙学镂…

中国医学健康管理数字化发展风向标——专家共话未来趋势

随着科技的飞速发展&#xff0c;数字化已经成为中国医学健康管理领域的重要发展方向。 2024年5月20日由中国管理科学研究院智联网研究所、中国民族医药协会医养教育委员会、国家卫健委基层健康服务站、中国老龄事业发展基金会、中国智联网健康管理系统平台、中国医学健康管理数…

express.js--token中间件验证及token解析(三)

主要作用 访问路由接口时&#xff0c;哪些需要校验token 通过token解析身份信息&#xff0c;就可以知道是哪个人 框架基本搭建express.js--基本用法及路由模块化(一)-CSDN博客 如何生成tokenexpress.js--生成token(二)-CSDN博客 middleware/index.js const jwt require(…

【openlayers系统学习】3.1-3.2彩色GeoTIFF图像渲染

一、彩色GeoTIFF图像渲染 Sentinel-2 卫星任务收集并传播覆盖地球陆地表面的图像&#xff0c;重访频率为 2 至 5 天。传感器收集多波段图像&#xff0c;其中每个波段都是电磁频谱的一部分。 2A 级 (L2A) 产品提供以下频段的表面反射率测量&#xff1a; BandDescriptionCentra…

LVDS与IDELAY

摘要&#xff1a;LVDS&#xff08;Low-Voltage Differential Signaling&#xff09;低电压差分信号&#xff0c;是一种低功耗、低误码率、低串扰和低辐射的差分信号技术&#xff1b;LVDS会被经常使用到&#xff0c;使用的过程中难免会碰到时序问题&#xff0c;需要借助IDELAY进…

【Qt问题】QStringLiteral、QString、QLatin1String简单区分

【Qt问题】QStringLiteral、QString、QLatin1String简单区分 最近在看项目代码的时候&#xff0c;一个频繁使用的关键词——“QStringLiteral”&#xff0c;引起了我的注意&#xff0c;关键是我竟然对它非常模糊&#xff0c;这不是一个Qter能够轻易接受的事情。 于是我秉承着…

前端学习-day08

文章目录 01-相对定位02-绝对定位03-绝对定位居中04-固定定位05-堆叠顺序06-CSS精灵-基本使用07-案例-京东服务08-字体图标10.垂直对齐方式11-过度12-透明度13-光标类型14-轮播图 01-相对定位 <!DOCTYPE html> <html lang"en"> <head><meta ch…

动态规划(算法)---01.斐波那契数列模型_第N个泰波那契数

前言&#xff1a; 有一个很著名的公式 “程序数据结构算法”。 算法是模型分析的一组可行的&#xff0c;确定的&#xff0c;有穷的规则。通俗的说&#xff0c;算法也可以理解为一个解题步骤&#xff0c;有一些基本运算和规定的顺序构成。但是从计算机程序设计的角度看&#xff…

【C++】牛客 ——NC138 矩阵最长递增路径

✨题目链接&#xff1a; NC138 矩阵最长递增路径 ✨题目描述 给定一个 n 行 m 列矩阵 matrix &#xff0c;矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径&#xff0c;使这条路径上的元素是递增的。并输出这条最长路径的长度。 这个路径必须满足以下条件&#…

【STM32项目】基于stm32智能鱼缸控制系统的设计与实现(完整工程资料源码)

实物演示效果 基于stm32智能鱼缸控制系统的设计与实现 目录&#xff1a; 实物演示效果 目录&#xff1a; 一、 绪论 1.1 项目研究目的及意义 1.1.1 选题目的 1.1.2 选题意义 1.2 国内外研究现状 1.2.1 国外发展现状 1.2.2 国内发展现状 1.3 项目研究内容 二、智能鱼缸系统总体设…

一文读懂Linux

前言 为了便于理解&#xff0c;本文从常用操作和概念开始讲起。虽然已经尽量做到简化&#xff0c;但是涉及到的内容还是有点多。在面试中&#xff0c;Linux 知识点相对于网络和操作系统等知识点而言不是那么重要&#xff0c;只需要重点掌握一些原理和命令即可。为了方便大家准…

2024年5月19日优雅草蜻蜓K知识付费系统旗舰版v1.0.9进度更新

v1.1.0更新 v1.1.0更新 2024年5月19日优雅草蜻蜓K知识付费系统旗舰版v1.0.9进度更新&#xff0c;首页体育栏目完善新增用户发布页面 开发进度 首页体育栏目完善 新增用户发布页面 新增用户登录完善 新增学习课程页面完善-过往课程数据完成 去掉其他三方登录&#xff0c;新增…

java文档管理系统的设计与实现源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的文档管理系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 文档管理系统的…

第二证券今日投资参考:5月国产游戏版号发放 猪价加速上涨

昨日&#xff0c;两市股指盘中震荡上扬&#xff0c;沪指盘中续创年内新高&#xff0c;创业板指一度涨超1%。到收盘&#xff0c;沪指涨0.54%报3171.15点&#xff0c;深证成指涨0.43%报9750.82点&#xff0c;创业板指涨0.59%报1875.93点&#xff0c;上证50指数涨0.34%&#xff1b…

小程序-收货地址管理模块实现

页面结构代码&#xff1a; address-form.vue --->新建地址和修改地址页面 <template><view class"content"><form><!-- 表单内容 --><view class"form-item"><text class"label">收货人</text>…

登录安全分析报告:创蓝云智注册

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

# 分布式链路追踪_skywalking_学习(1)

分布式链路追踪_skywalking_学习&#xff08;1&#xff09; 一、APM 系统概述 1、什么是 APM 系统&#xff1f; APM &#xff1a;全称 Application Performance Management 即应用性能管理系统。是对企业系统即时监控以实现对应用程序性能管理和故障管理的系统化的解决方案。…

JS对象超细

目录 一、对象是什么 1.对象声明语法 2.对象有属性和方法组成 二、对象的使用 1.对象的使用 &#xff08;1&#xff09;查 &#xff08;2&#xff09;改 &#xff08;3&#xff09;增 &#xff08;4&#xff09;删&#xff08;了解&#xff09; &#xff08;5&#xf…

算网融合,移动云加速形成新质生产力

文章目录 一.移动云介绍1.政策支持2.产品介绍image-20240522164613917 二.云主机ESC使用1.实名认证2.云主机订购3.登录方式4.控制台登录5.Xshell登录6.简单使用 三.产品使用评价1.真伪测评2.产品优势 一.移动云介绍 1.政策支持 ​ 正所谓家事国事天下事&#xff0c;事事关心&…