【Go语言精进之路】构建高效Go程序:了解map实现原理并高效使用
文章目录
- 引言
- 一、什么是`map`
- 1.1 `map`的基本概念与特性
- 1.2 map的初始化与零值问题
- 1.3 `map`作为引用类型的行为
- 二、`map`的基本操作
- 2.1 插入数据
- 2.2 获取数据个数
- 2.3 查找和数据读取
- 2.4 删除数据
- 2.5 遍历数据
- 三、map的内部实现
- 3.1 初始状态
- 3.2 `map`扩容
- 3.3 `map`并发
- 四、尽量使用cap参数创建map
- 五、总结
引言
在Go语言中,
map
是一种无序的键值对集合,它以其高效的查找、插入和删除操作而闻名。了解map
的基本概念、特性和内部实现机制,对于编写高效且稳定的Go代码至关重要。本文将深入探讨map
的各个方面,包括其初始化、基本操作、内部实现细节,并讨论为何在创建map
时应尽量使用带有容量提示参数的做法。
一、什么是map
1.1 map
的基本概念与特性
map
是Go语言中的一种内建引用类型,它表示一组无序的键值对集合。每个键值对用冒号“:”分隔,其中键(key)是唯一的,用于标识对应的值(value)。map
允许我们根据特定的键快速检索、更新或删除对应的值。
在Go语言中,map
对值(value)的数据类型没有特定限制,它可以是任意类型,包括基本类型、结构体、自定义类型等。但是,键(key)的类型有严格要求:key的类型必须可以通过“==”和“!=”操作符进行比较,这意味着键的类型需要是可比较的。因此,像函数、map和切片这样不可比较的类型不能作为map的键。
1.2 map的初始化与零值问题
需要注意的是,map
类型不支持“零值可用”,也就是说,未显式初始化的map变量其默认值为nil
。尝试对nil
的map
变量进行操作将会导致运行时错误(panic)。例如:
var m map[string]int // 此时m的值为nil
// 下面的操作将会导致运行时panic,因为m未被初始化
m["key"] = 1 // panic: assignment to entry in nil map
为了避免这种情况,我们需要在使用map
之前对其进行初始化。可以通过以下两种方式之一来初始化map
:
- 使用
make
函数初始化:
m := make(map[string]int)
m["key"] = 1 // 现在这是安全的,因为m已经被初始化
- 使用字面量初始化:
m := map[string]int{"key": 1}
// 或者
m := map[string]int{}
m["key"] = 1 // 同样是安全的,因为m已经被初始化
初始化后的map
可以被安全地用于存储和检索键值对,而不会导致运行时错误。在Go程序中,map
是非常有用的数据结构,特别适用于需要根据键快速查找、添加或删除相应值的场景。
1.3 map
作为引用类型的行为
和切片一样,map
也是引用类型。这意味着,当你将一个map
类型的变量传递给函数时,实际上传递的是指向底层数据结构的指针,而不是整个数据结构的拷贝。因此,将map
类型变量作为函数参数传入不会有很大的性能消耗。
此外,由于在函数内部和外部引用的是同一个底层数据结构,所以在函数内部对map
变量的修改(如添加、删除键值对或更新值)在函数外部也是可见的。这种特性使得map
在需要在多个函数或方法间共享和修改数据时非常有用。
以下是一个示例,展示了在函数内部修改map
,并在函数外部观察到这些修改:
package mainimport "fmt"func modifyMap(m map[string]int) {// 在函数内部修改mapm["apple"] = 5m["banana"] = 10
}func main() {// 初始化一个mapfruitMap := make(map[string]int)// 调用函数,传入map作为参数modifyMap(fruitMap)// 打印修改后的map,可以看到在modifyMap函数中所做的修改fmt.Println(fruitMap) // 输出: map[apple:5 banana:10]
}
在这个例子中,modifyMap
函数接收一个map
作为参数,并在函数内部添加了两个键值对。当函数执行完毕后,main
函数中的fruitMap
已经被修改,反映了modifyMap
函数中所做的更改。这是因为map
是引用类型,modifyMap
接收的是fruitMap
的引用,因此对它的任何修改都会反映在原始map
上。
二、map
的基本操作
2.1 插入数据
当面对一个非nil
的map
类型变量时,我们可以向其中插入符合map
类型定义的任意键值对。值得注意的是,如果试图插入的键(key)已经存在于map
中,那么新的值将会覆盖旧的值。Go运行时会管理map
内部的内存,因此,除非系统内存耗尽,否则我们不必担心向map
中插入大量数据。
m := make(map[string]int)
m["apple"] = 5 // 插入键值对 "apple": 5
m["apple"] = 7 // 更新键 "apple" 的值为 7,旧值5被覆盖
m["banana"] = 10 // 插入键值对 "banana": 10
在上述代码中,我们首先创建了一个从string
类型到int
类型的map
。然后,我们插入了键值对"apple": 5
。紧接着,我们尝试再次插入键"apple"
,但这次赋予它一个新的值7
。由于这个键已经存在于map
中,因此旧的值5
会被新的值7
覆盖。最后,我们插入了一个新的键值对"banana": 10
。
这种覆盖行为是map
的一个重要特性,它允许我们根据需要更新存储在map
中的值。在实际编程中,这一特性非常有用,比如当我们需要根据某些条件动态改变值时。
2.2 获取数据个数
要获取map
中数据的个数,可以使用内置的len()
函数。
count := len(m)
fmt.Println("Number of items in map:", count) // 输出map中的元素个数
len(m)
返回m
中当前存储的键值对数量。
2.3 查找和数据读取
可以根据键来查找和读取map
中的数据。如果键不存在,则返回该类型的零值。
value, exists := m["apple"] // 查找键为"apple"的值,并检查键是否存在
if exists {fmt.Println("The value of 'apple' is:", value)
} else {fmt.Println("'apple' does not exist in the map.")
}
使用value, exists := m[key]
的格式可以同时获取键对应的值和该键是否存在。如果键存在,exists
为true
,并且value
为该键对应的值;如果键不存在,exists
为false
,value
为该类型的零值。
2.4 删除数据
要从map
中删除一个键值对,可以使用delete()
函数。
delete(m, "banana") // 删除键为"banana"的键值对
delete(m, key)
函数会从m
中删除与key
关联的键值对。如果key
不存在,则delete
什么也不做。
2.5 遍历数据
可以使用range
关键字来遍历map
中的所有键值对。
package mainimport "fmt"func main() {m := map[int]int{1: 11, 2: 12, 3: 13,}fmt.Printf("{ ")for key, value := range m {fmt.Printf("key: %d, value: %d ", key, value)}fmt.Printf(" }\n")
}
range m
会迭代m
中的所有键值对,每次迭代都会返回当前的键和值。在上面的循环中,key
和value
分别被赋值为当前迭代的键和值,然后打印出来。
上面的输出结果非常理想,给我们的表象是迭代器按照map
中的元素插入次序逐一遍历。那让我们再多遍历几次这个map
:
package mainimport "fmt"func doIteration(m map[int]int) {fmt.Printf("{ ")for key, value := range m {fmt.Printf("key: %d, value: %d ", key, value)}fmt.Printf(" }\n")
}func main() {m := map[int]int{1: 11,2: 12,3: 13,}for i := 0; i < 3; i++ {doIteration(m)}
}
我们看见对同一map
进行多次遍历,遍历的元素次序并不相同。这是因为Go运行时在初始化map迭代器时对起始位置做了随机处理。因此千万不要依赖遍历map
所得到的元素次序。
三、map的内部实现
和切片相比,map
类型的内部实现要复杂得多。Go运行时使用一张哈希表来实现抽象的map
类型,运行时实现了map
操作的所有功能,包括查找、插入、删除、遍历等。本文这里只做一些简单的介绍。
3.1 初始状态
在Go语言中,当一个map
被初始化时,它会分配一个较小的内存空间来存储键值对数据。这个初始的内存空间包含一定数量的桶(buckets),每个桶能够存储一个或多个键值对。初始状态下,这些桶都是空的。
map
的初始化可以通过字面量、make
函数或者直接使用map
类型进行。例如:
// 使用字面量初始化
m1 := map[string]int{"apple": 5, "banana": 10}// 使用make函数初始化
m2 := make(map[string]int)// 直接声明map类型变量(需要后续进行初始化)
var m3 map[string]int
m3 = make(map[string]int)
在初始化时,map
会预留一定的空间以准备存储键值对,但这个初始空间相对较小。
3.2 map
扩容
当map
中的元素数量增加,负载因子(已存储的键值对数量与桶的数量的比例)也会随之增加。当负载因子超过某个预定的阈值时,map
会进行扩容以保证性能。
扩容过程中,map
会创建一个更大的桶数组,并且重新计算所有现有键值对的哈希值,将它们重新分布到新的桶数组中。这个重新哈希和分布的过程是为了确保键值对能够更均匀地分散在新的桶中,从而减少哈希冲突并提高查找效率。
扩容是一个相对昂贵的操作,因为它涉及到内存分配和大量数据的迁移。因此,在实际使用中,如果可能的话,最好提前预估map
的大小并一次性分配足够的空间。
3.3 map
并发
Go语言的map
类型并不是并发安全的。这意味着如果多个goroutine同时对一个map
进行读写操作,就可能导致数据竞争(data race)和不可预知的行为。
为了在并发环境中安全地使用map
,有几种常见的解决方案:
-
使用互斥锁(Mutex):通过使用
sync.Mutex
或sync.RWMutex
来同步对map
的访问。在每次读写map
之前,先获取锁,操作完成后再释放锁。 -
使用
sync.Map
:Go语言标准库提供了一个并发安全的map
实现,即sync.Map
。它内部使用了分段锁和其他优化技术来提供高效的并发访问。 -
通道(Channel):另一种方法是使用Go的通道来序列化对
map
的访问。通过将所有对map
的操作都通过一个或多个通道来进行,可以确保在同一时间只有一个goroutine能够访问map
。
在实际应用中,选择哪种并发控制方法取决于具体的使用场景和性能要求。对于简单的用例,使用互斥锁可能就足够了;而在需要高并发性能的场景中,sync.Map
可能更为合适。
四、尽量使用cap参数创建map
由于扩容是一个相对昂贵的操作,因为它涉及到内存分配和大量数据的迁移,因此,如果可以的话我们最好对map
使用规模做出粗略的估算,并使用cap
参数对map
实例进行初始化。
当你创建一个 map
而不指定容量时,Go 会自动为你分配一个初始的、未指定的容量。这个容量足以满足初始需求,并且随着 map
中元素的增加,Go 的运行时会自动管理其内部结构的大小调整,以容纳更多的元素。这是最常见也是最简单的初始化方式。
m := make(map[string]int)
如果你在创建 map
时明确指定了 cap
参数,你是在给 Go 提供一个关于你期望 map
最终可能包含多少个键值对的提示。这有助于减少 map
在增长过程中需要重新分配内存的次数,从而提高效率,尤其是在你知道 map
大致会有多大时。但请注意,指定的 cap
是一个提示而不是严格的限制,map
的实际容量可能会略高于指定的值,且 map
仍然可以在达到这个预设容量后继续增长。
m := make(map[string]int, 100)
优缺点分析:
- 不使用 cap:简化初始化过程,让Go自动管理容量,适用于大多数情况,特别是当你不确定
map
最终大小时。 - 使用 cap:通过预先估计
map
的大小,可以略微优化性能,减少动态扩容的次数,适合于明确知道或能估算map
容量的场景。
选择是否使用 cap
主要取决于你对map
最终规模的了解程度和对性能的特定需求。在不需要精确控制初始容量的情况下,省略 cap
是一个简洁且有效的方法。然而,如果你正处理大量数据且关心性能优化,明智地设定初始容量可以带来益处。
下面对两种初始化方式的性能进行对比:
package mainimport "testing"const mapSize = 10000func BenchmarkMapInitWithoutCap(b *testing.B) {for i := 0; i < b.N; i++ {m := make(map[int]int)for i := 0; i < mapSize; i++ {m[i] = i}}
}
func BenchmarkMapInitWithCap(b *testing.B) {for i := 0; i < b.N; i++ {m := make(map[int]int, mapSize)for i := 0; i < mapSize; i++ {m[i] = i}}
}
BenchmarkMapInitWithoutCap
函数执行以下操作:
-
它使用一个循环,该循环将运行
b.N
次,其中b.N
是testing.B
提供的,表示基准测试应该运行的次数。这是为了确保我们获得足够的数据点来平均性能测试结果,从而获得更准确的数据。 -
在每次循环中,它创建一个新的map,没有指定初始容量(
make(map[int]int)
)。 -
然后,它向这个map中插入
mapSize
(即10000)个键值对,其中键和值都是循环变量i
。
这个基准测试的目的是测量在不指定初始容量的情况下,初始化并填充一个map的性能。
执行结果如下:
BenchmarkMapInitWithCap
函数与BenchmarkMapInitWithoutCap
非常相似,但有一个关键区别:
- 在创建map时,它使用
make(map[int]int, mapSize)
来指定一个初始容量提示,这个容量提示等于将要插入的键值对的数量(即10000)。
这个基准测试的目的是测量在指定了与将要插入的键值对数量相等的初始容量提示的情况下,初始化并填充一个map的性能。
下面是执行结果:
可以看出,使用cap
参数的map
实例的平均写性能是不使用cap
参数的2倍。
五、总结
本文通过详细阐述了Go语言中 map
的基本概念、特性及其作为引用类型的行为,介绍了 map
的基本操作如插入、获取数据个数、查找、删除和遍历数据等。同时,深入剖析了 map
的内部实现,包括其初始状态、扩容机制以及并发问题。最后,本文强调了在使用 map
时,为了提高性能和减少内存重新分配的次数,应尽量在创建时提供合理的容量提示参数。通过全面理解 map
的工作原理和最佳实践,开发者可以更加有效地利用这一强大的数据结构来优化程序性能。
相关文章:
【Go语言精进之路】构建高效Go程序:了解map实现原理并高效使用
🔥 个人主页:空白诗 🔥 热门专栏:【Go语言精进之路】 文章目录 引言一、什么是map1.1 map的基本概念与特性1.2 map的初始化与零值问题1.3 map作为引用类型的行为 二、map的基本操作2.1 插入数据2.2 获取数据个数2.3 查找和数据读取…...
【机器人和人工智能——自主巡航赛项】进阶篇
文章目录 案例要求创建地图rviz仿真 保存地图坐标点定位识别训练主逻辑理解语音播报模块匹配二维码识别多点导航讲解视频其余篇章 案例要求 创建地图 ./1-gmapping.sh 把多个launch文件融合在sh文件里面 rviz仿真 rviz是rose集成的可视化界面,查看机器人的各项数…...
[大师C语言(第二十五篇)]C语言字符串探秘
引言 字符串是编程中不可或缺的基本数据类型之一,它用于表示和操作文本数据。在C语言中,字符串以一种独特的方式实现,与许多其他编程语言的处理方式不同。本文将深入探讨C语言字符串背后的技术,帮助你更好地理解和应用字符串。 …...
xLua(一) 环境安装笔记
为了方便查阅记录一下xLua的安装地址及方法 1.登录地址下载: https://github.com/Tencent/xLua 2.解压文件 将文件中的这些内容拷贝到项目中的Asset文件夹中 注意 : 工程项目路径不得含有中文 3.将Tools复制到Asset同级目录下 4.导入后会发现有Bug,需要导入工程 5.还有另…...
Python基础教程(十一):数据结构汇总梳理
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝Ὁ…...
制造型企业图纸泄露问题,如何从根源解决核心文件资料泄露问题?
制造型企业最核心重要的文件当然是图纸,图纸在制造型企业中起到关键主导地位,如果图纸泄露与研发性企业源代码被泄露一样的严重,制造型企业如何保护核心图纸不被外泄是企业在日常经营过程中的重要组成部分,现在有很多防止泄露的产…...
英伟达最新GPU和互联路线图分析
Nvidia在计算、网络和图形领域独树一帜,其显著优势在于雄厚的资金实力及在生成式人工智能市场的领先地位。凭借卓越的架构、工程和供应链,Nvidia能够自由实施创新路线图,引领行业未来。 到 21 世纪,Nvidia 已经是一个非常成功的创…...
Github 2024-06-10 开源项目日报 Top10
根据Github Trendings的统计,今日(2024-06-10统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目2Go项目2PHP项目1Blade项目1TypeScript项目1Lua项目1Dart项目1Swift项目1Cuda项目1Python项目1MDX项目1Ventoy: 100%开源的可启动USB解决方…...
前后端分离项目中Spring Boot返回的时间与前端相差8个小时
概述 今天在做一个前后端分离项目时,发现从后端获取的时间与从数据库获取的时间相差八个小时,最终排查后发现由于Springboot使用本地时区导致,修改SpringBoot时区后解决 环境 MySQL8SpringBoot 原因排查 发现从后端获取的数据总是比前端快八个小时 …...
stm32MP135裸机编程:使用USB/UART烧录程序到SD卡并从SD卡启动点亮一颗LED灯
0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf STM32CubeProgrammer v2.16.0 烧录需要的二进制文件1 烧录到SD卡需要哪些文件 参考《轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序》,烧录需要的SD卡文件如下: &a…...
【NoSQL数据库】Redis Cluster集群(含redis集群扩容脚本)
Redis Cluster集群 Redis ClusterRedis 分布式扩展之 Redis Cluster 方案功能数据如何进行存储 redis 集群架构集群伸缩向集群中添加一个新的master节点,并向其中存储 num10 .脚本对redis集群扩容缩容,脚本参数为redis集群,固定从6001移动200…...
重邮计算机网络803-(2)物理层
一.物理层 1.介绍 物理层的主要任务描述为确定与传输媒体的接口的一些特性,即: ①机械特性 指明接口所用接线器的形状和尺寸、引线数目和排列、固定和锁定装置等等。 ②电气特性 指明在接口电缆的各条线上出现的电压的范围。 ③功能特性 指明某条线上…...
uniapp使用webview内嵌H5的注意事项
一、描述 uniapp项目中构建app,需要内嵌H5页面,在使用webview时,遇到了以下几个问题: 内嵌H5,默认全屏显示;内嵌页面遮挡住了app的自定义tabbar组件;样式修改无效; 二、解决方案&a…...
现代 C++的高效并发编程模式
现代C提供了许多高效的并发编程模式,以满足日益增长的多核和分布式系统的需求。以下是一些常用的高效并发编程模式: 异步编程:使用std::async来创建异步任务,可以在后台执行任务,将结果返回给调用者。 并行编程&#…...
汇编语言作业(五)
目录 一、实验目的 二、实验内容 三、实验步骤以及结果 四、实验结果与分析 五、 实验总结 一、实验目的 1.熟悉掌握汇编语言的程序结构,能正确书写数据段、代码段等 2,利用debug功能,查看寄存器(CS,IP,AX,DS..)及数据段的…...
收音机的原理笔记
1. 收音机原理 有线广播:我们听到的声音是通过空气振动进行传播,因此可以通过麦克风(话筒)将这种机械振动转换为电信号,传到远处,再重新通过扬声器(喇叭)转换为机械振动,…...
排序算法案例
排序算法概述 排序算法是计算机科学中的一个重要主题,用于将一组数据按特定顺序排列。排序算法有很多种,每种算法在不同情况下有不同的性能表现。不同的排序算法适用于不同的场景和数据特征。在选择排序算法时,需要考虑数据规模、数据分布以…...
时间序列评价指标
评价指标 均方误差( M S E MSE MSE) 定义:预测值与实际值之间差异的平方和的平均值。公式: ( M S E 1 n ∑ i 1 n ( y i − y ^ i ) 2 ) (MSE \frac{1}{n}\sum_{i1}^{n}(y_i - \hat{y}_i)^2) (MSEn1∑i1n(yi−y^i)…...
Docker:安装 Orion-Visor 服务器运维的技术指南
请关注微信公众号:拾荒的小海螺 博客地址:http://lsk-ww.cn/ 1、简述 Orion-Visor 是一种用于管理和监控容器的工具。它提供了一个直观的界面,用于查看容器的状态、资源使用情况以及日志等信息。在这篇技术博客中,我们将介绍如何…...
HarmonyOS Next 系列之底部标签栏TabBar实现(三)
系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现(一) HarmonyOS Next 系列之验证码输入组件实现(二) HarmonyOS Next 系列之底部标签栏TabBar实现(三) 文章目录 系列文章目录前言一、实现原理二、…...
mac怎么录制屏幕?这2个方法你值得拥有
在数字化时代,屏幕录制已经成为一种常见且重要的工具,无论是教学演示、游戏直播还是会议记录,屏幕录制都发挥着不可或缺的作用。对于Mac用户而言,如何高效、便捷地进行屏幕录制,是一个值得探讨的话题,可是很…...
爱德华三坐标软件ACdmis.AC-dmis密码注册机
爱德华三坐标软件 AC-DMIS 是一款功能强大的三坐标测量软件,具有以下特点: • 支持多种测量模式:包括接触式测量、非接触式测量、复合式测量等,可以满足不同类型工件的测量需求。 • 高精度测量:采用先进的测量算法和…...
计算机网络 期末复习(谢希仁版本)第3章
对于点对点的链路,目前使用得最广泛的数据链路层协议是点对点协议 PPP (Point-to-Point Protocol)。局域网的传输媒体,包括有线传输媒体和无线传输媒体两个大类,那么有线传输媒体有同轴电缆、双绞线和光纤;无线传输媒体有微波、红…...
代码随想录——数组
给定一个n个元素有序(升序)的整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1. //这个题说实话从逻辑上来看实在是太简单了,但是为什么每一次我写起来都感…...
计算机网络7——网络安全4 防火墙和入侵检测
文章目录 一、系统安全:防火墙与入侵检测1、防火墙1)分组过滤路由器2)应用网关也称为代理服务器(proxy server), 二、一些未来的发展方向 一、系统安全:防火墙与入侵检测 恶意用户或软件通过网络对计算机系统的入侵或攻击已成为当今计算机安…...
html+CSS+js部分基础运用20
根据下方页面效果如图1所示,编写程序,代码放入图片下方表格内 图1.效果图 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" conte…...
ISO 19115-2:2019 附录C XML 模式实现
C.1 XML 模式 本文件中定义的 UML 模型的 XML 模式在 ISO/TS 19115-3 中定义的适当 XML 命名空间中提供。新增内容包括: 命名空间前缀模式文件名Metadata for ACquisition (mac)acquisitionInformationImagery.xsdMetadata for Resource Content (mrc)contentInfo…...
DevOps的原理及应用详解(一)
本系列文章简介: 在当今快速变化的商业环境中,企业对于软件交付的速度、质量和安全性要求日益提高。传统的软件开发和运维模式已经难以满足这些需求,因此,DevOps(Development和Operations的组合)应运而生&a…...
【冲刺秋招,许愿offer】第 三 天(水一天)
【冲刺秋招,许愿offer】第 二 天(水一天) 知识点牛客emo 知识点 今天端午,上午去摘杏下午理发,一天没咋看电脑。晚上刷刷LeetCode看看八股。 牛客 spring事务失效的情况 捕获到异常,自己手动处理 方法修…...
使用 C# 学习面向对象编程:第 6 部分
继承 亲爱的读者,继承意味着从源头继承一些东西。例如,儿子可以继承父亲的习惯。同样的概念也用于面向对象编程;它是 OOP 的第二大支柱。 继承允许创建一个新类,该新类继承另一个类或基类的属性,继承这些成员的类称为…...
宁波规划建设局网站/西安网站关键词排名
微信回复图片 注意是mediaId,不是messageId; 这个注意了,其他与文本消息一致; package com.tuling.model;import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml…...
做软件怎么赚钱/百度seo关键词点击软件
总结了一下关于使用onmouseover以及onmouseout会出现的bug 首先简单的布局: <div id"box"><div>这是一个内容</div> </div> 简单写了一下样式,效果如下: js代码如下: var oBox document.getEl…...
wordpress怎么添加文章/百度推广怎么看关键词排名
今天从同事那里发现个整理桌面的好东东---Fences,对于习惯把桌面当工作区的我来说,确实方便了许多,再也不用自己创建文件夹管理图标了 官网介绍 http://www.stardock.com/products/fences/ 免费下载 http://www.stardock.com/products/fences…...
湘潭网站建设哪些公司/营销计划怎么写
Trick 【题目描述】 暴躁的稻草人,最终以自爆来给我们的队伍致命一击,全队血量见底,稻草人也一分为二。还好我们有雨柔妹子,瞬间精力回满。不过事后姜小弟和龙腹黑就开始了报复。 他们读取存档,将若干暴躁的稻草人活捉…...
贵阳专业做网站的公司/市场营销策划方案3000字
科目编号:8638 座位号 2017-2018学年度第二学期期末考试 薪酬制度与薪酬管理 试题 2018年 7 月 一、单选题(本大题共10小题,每小题3分,共计30分) (★请考生务必将答案填入到下面对应序号的答题框中★&…...
怎么样免费做网站/平台运营
原标题:LOL最强的钩子是谁的?不是机器人,也不是锤石,而是他!在LOL中,最具功能性的技能,应该就是那些钩人的技能了。这些有钩子技能的英雄,不管是开团,还是保人࿰…...