当前位置: 首页 > news >正文

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切片与技巧(附图解)

相关文章:

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

1.什么是切片 在 Go 语言中的切片&#xff08;slice&#xff09;是一种灵活的动态数组&#xff0c;它可以自动扩展和收缩&#xff0c;是 Go 语言中非常重要的数据结构之一。切片是基于数组实现的&#xff0c;它的底层是数组&#xff0c;可以理解为对底层数组的抽象。它会生成一…...

2024 中青杯高校数学建模竞赛(A题)数学建模完整思路+完整代码全解全析

你是否在寻找数学建模比赛的突破点&#xff1f;数学建模进阶思路&#xff01; 作为经验丰富的数学建模团队&#xff0c;我们将为你带来2024 长三角高校数学建模竞赛&#xff08;A题&#xff09;的全面解析。这个解决方案包不仅包括完整的代码实现&#xff0c;还有详尽的建模过…...

开源与闭源:AI模型发展的双重路径之争

前言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI模型的应用已经渗透到各行各业&#xff0c;从医疗、金融到制造、教育&#xff0c;无不受到AI技术的深刻影响。在讨论一个AI模型“好不好”“有没有发展”时&#xff0c;绕不过“开源”和“闭源”两条…...

微信小程序---小程序文档配置(2)

一、小程序文档配置 1、小程序的目录结构 1.1、目录结构 小程序包含一个描述整体程序的 app 和多个描述各自页面的 page 一个小程序主体部分由三个文件组成&#xff0c;必须放在项目的根目录 比如当前我们的《第一个小程序》项目根目录下就存在这三个文件&#xff1a; 1…...

15:00面试,15:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

电磁兼容(EMC):去耦电容设计详解

目录 1. 概念 2. 去耦电容工作机理 3. 去耦电容大小选择 4. 去耦电容PCB布局 电容在电路中不同作用有不同的称呼去耦电容、旁路电容、储能电容&#xff0c;而这些作用又可以统称为滤波。本文将详细解读一下三者之间的差别&#xff0c;并着重说明一下去耦电容的设计方法。 …...

《数组逆序输出》

描述 编写程序&#xff0c;输入10个整数n存入&#xff0c;再按逆序重新存放后再输出。 输入描述 输入共10个数。 输出描述 输出共1行&#xff0c;每个数字用空格隔开。 样例输入 1 -5 -4 -3 -2 -1 0 1 2 3 4 样例输出 1 4 3 2 1 0 -1 -2 -3 -4 -5 提示 对于100%的数据…...

必应崩了?

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

Elasticsearch集群和Logstash、Kibana部署

1、 Elasticsearch集群部署 服务器 安装软件主机名IP地址系统版本配置ElasticsearchElk10.3.145.14centos7.5.18042核4GElasticsearchEs110.3.145.56centos7.5.18042核3GElasticsearchEs210.3.145.57centos7.5.18042核3G 软件版本&#xff1a;elasticsearch-7.13.2.tar.gz 示…...

网络的基础理解

文章目录 网络的基础认识 网络协议协议分层OSI七层模型TCP/IP 五层/四层 模型 网络的基础认识 先来看下面几个问题 什么是网络&#xff1f; 网络就是有许多台设备包括计算机单不仅限于计算机&#xff0c;这些设备通过相互通信所组成起来系统&#xff0c;我们称之为网络所以如…...

Android Studio 与 Gradle 及插件版本兼容性

Android Studio 开始新项目时&#xff0c;会自动创建其中部分文件&#xff0c;并为其填充合理的默认值。 项目文件结构布局&#xff1a; 一、Android Gradle 及插件作用&#xff1a; Android Studio 构建系统以 Gradle 为基础&#xff0c;并且 Android Gradle 插件 (AGP) 添加…...

【BUG】Edge|联想电脑 Bing 搜索报错“Ref A: 乱码、 Ref B:乱码、Ref C: 日期” 的解决办法

文章目录 省流版前言解决办法 详细解释版前言问题描述与排查过程解决办法与总结 省流版 我原以为我解决了&#xff0c;才发的博客&#xff0c;晚上用了一下其他设备发现还是会出现这个问题… 这篇博客并未解决该问题&#xff0c;如果评论里有人解决了这个问题不胜感激&#x…...

深度学习小车操作手册全

深度学习小车_操作手册_全 资源链接 分享文件&#xff1a;深度学习小车_操作手册_全.pdf 链接&#xff1a;https://pan.xunlei.com/s/VNy-KXPDZw64RqQGXiWVEDMRA1?pwdymu4# 复制这段内容后打开手机迅雷App&#xff0c;查看更方便智能车简介 2019 年的特斯拉自动驾驶开放日上…...

Python实现天气数据采集

Python实现天气数据采集 一、需求介绍二、完整代码一、需求介绍 本次天气数据采集的需求是获取每日的最高温、最低温、风力、风向、天气状况、AQI指数,如图所示,完整代码附后: 本次采集的目标网址是2345天气网: 上图的URL中,beijing是城市名称的缩写,54511即为城市代码…...

05 JavaSE-- 异常、IOStream、多线程、反射、Annotation、泛型、序列化

Exception 异常 异常也是对象&#xff0c;也有自己的体系&#xff0c;在这个体系中&#xff0c;所有异常对象的根类是 throwable 接口。异常和 error 错误是不同的概念。 错误是严重的 JVM 系统问题&#xff0c;一般不期待程序员去捕获、处理这些错误&#xff0c;同时&#xf…...

c++/c语法基础【2】

目录 1.memset 数组批量赋值 2.字符数组 ​编辑输入输出: 字符数组直接输入输出%s: gets! string.h 1.strlen:字符串去掉末尾\0的长度...

python 庆余年2收视率数据分析与可视化

为了对《庆余年2》的收视率进行数据分析与可视化&#xff0c;我们首先需要假设有一组收视率数据。由于实际数据可能无法直接获取&#xff0c;这里我们将使用模拟数据来演示整个过程。 以下是一个简单的步骤&#xff0c;展示如何使用Python&#xff08;特别是pandas和matplotli…...

yolov8训练自己数据集时出现loss值为nan。

具体原因目前暂未寻找到。 解决办法 将参数amp改成False即可。 相关资料&#xff1a; https://zhuanlan.zhihu.com/p/165152789 https://github.com/ultralytics/ultralytics/issues/1148...

[Chapter 5]线程级并行,《计算机系统结构》,《计算机体系结构:量化研究方法》

文章目录 一、互连网络1.1 互连网络概述1.1 互连函数1.1.1 互连函数1.1.2 几种基本的互连函数1.1.2.1 恒等函数1.1.2.2 交换函数1.1.2.3 均匀洗牌函数1.1.2.4 碟式函数1.1.2.5 反位序函数1.1.2.6 移数函数1.1.2.7 PM2I函数 1.2 互连网络的结构参数与性能指标1.2.1 互连网络的结…...

首发!飞凌嵌入式FETMX6ULL-S核心板已适配OpenHarmony 4.1

近日&#xff0c;飞凌嵌入式在FETMX6ULL-S核心板上率先适配了OpenHarmony 4.1&#xff0c;这也是业内的首个应用案例&#xff0c;嵌入式核心板与OpenHarmony操作系统的结合与应用&#xff0c;将进一步推动千行百业的数智化进程。 飞凌嵌入式FETMX6ULL-S核心板基于NXP i.MX 6ULL…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...