数组,切片和字典

本文深入探讨Go语言中的数组、切片与字典三种基本数据结构,详细讲解了它们的定义、初始化、操作及使用场景,是Go语言学习者不可多得的参考资料。

1、数组(Array)

数组是一个有相同数据类型的元素组成的固定长度的有序集合,如:var x [5]int,使用var定义的数组,默认值如果整型为0,浮点型为0.0,字符串为"",对象为nil。

求整型数组之和,使用range函数,遍历数组、切片、字典时,返回索引和元素值;当对某个返回值不感兴趣时,要用下划线(_)来代替

package main

import "fmt"

func main() {
    var x [5]int
    x[0] = 2
    x[1] = 3
    x[2] = 3
    var sum int
    for _, elem := range x {
        sum += elem
    }
    fmt.Println(sum)
}

定义数组其他方式:1)、var x = [5]int{1,2,3,4,5}

         2)、var x =[5]int{}

             x[0]=1

         3)、var x = [...]string{                 var x = [...]string{
              "Monday",                    "Monday", 

              "Tuesday"                    "Tuesday",//可以带着英文逗号
            }                        }

2、切片(Slice)

切片类似数组,切片长度是可变的;切片是有容量(capacity)和长度(length)两个属性的;切片变量使用时,索引不能大于等于切片长度,否则报错out of range

1)、make函数定义切片

package main

import "fmt"

func main() {
    var x = make([]float64, 5) //切片的容量和长度都是5
    fmt.Println("Capacity:", cap(x), "Length:", len(x))
    var y = make([]float64, 5, 10) //切片容量为10,长度为5
    fmt.Println("Capacity:", cap(y), "Length:", len(y))

    for i := 0; i < len(x); i++ {
        x[i] = float64(i)
    }
    fmt.Println(x)

    for i := 0; i < len(y); i++ {
        y[i] = float64(i)
    }
    fmt.Println(y)
}

输出如图:

2)、通过数组切片赋值,采用[low_index:high_index]方式截取数组来获取数值切片,其中获取包括low_index,不包括high_index,high_index不能大于数组长度

使用案例如下

package main

import "fmt"

func main() {
    var arr1 = [5]int{1, 2, 3, 4, 5}
    var s1 = arr1[2:3]
    var s2 = arr1[:3]
    var s3 = arr1[2:]
    var s4 = arr1[:]
    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
    fmt.Println(s4)
}

运行结果如下

3)、切片长度可变性,使用append函数

package main

import "fmt"

func main() {
    var arr1 = make([]int, 5, 10)
    for i := 0; i < len(arr1); i++ {
        arr1[i] = i
    }
    fmt.Println(arr1)

    arr1 = append(arr1, 5, 6, 7, 8, 9, 10)
    fmt.Println("Capacity:", cap(arr1), "Length:", len(arr1))
    fmt.Println(arr1)
}

运行结果如图,可知切片扩容算法是原来容量大小的两倍;在测试代码中发现,如果追加元素超过两倍容量,后面扩容算法就不会是倍数扩容了

4)、copy函数,复制切片中元素

package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3, 4, 5, 6}
    slice2 := make([]int, 5, 10)
    copy(slice2, slice1)
    fmt.Println(slice1)
    fmt.Println(slice2)
}

运行结果如下,slice2长度为5,所以只拷贝了slice1的前五个元素

5)、小总结一下,使用 := 方式定义切片,和数组区别仅仅是方括号[]中是否有数字或 ...

示例如下,输出结果都是:[1 2 3 4]

package main

import "fmt"

func main() {
    arr1 := [4]int{1, 2, 3, 4}
    fmt.Println(arr1)
    arr2 := [...]int{1, 2, 3, 4}
    fmt.Println(arr2)
    arr3 := []int{1, 2, 3, 4}
    fmt.Println(arr3)
}

3、字典(Map)

 字典是一组无序的,键值对的集合,字典也叫关联数组,数组通过索引取数,字典通过键来取数

1)、初始化数据方式定义字典

package main

import "fmt"

func main() {
    var x = map[string]string{
        "A": "Apple",
        "B": "Banana",
        "O": "Orange",
        "P": "Pear",
    }

    for key, val := range x {
        fmt.Println("Key:", key, "Value:", val)
    }
}

运行结果

 2)、make函数定义map,如果只定义,不初始化,使用会报错

package main

import "fmt"

func main() {
    var x map[string]string  //定义map
    x = make(map[string]string)  //初始化map

    x["A"] = "Apple"
    x["B"] = "Banana"
    x["O"] = "Orange"
    x["P"] = "Peer"

    for key, val := range x {
        fmt.Println("Key:", key, "Value:", val)
    }
}

3)、使用 := 定义并初始化map

package main

import "fmt"

func main() {
    x := make(map[string]string)

    x["A"] = "Apple"
    x["B"] = "Banana"
    x["O"] = "Orange"
    x["P"] = "Peer"

    for key, val := range x {
        fmt.Println("Key:", key, "Value:", val)
    }
}

4)、获取字典中不存在的key值,如果是整数则返回0,字符串返回 "";这样的返回值,如果map中就是有值为0或空字符串,就无法区分了

使用 := 可以获取map中元素值和是否存在的布尔类型,如下

package main

import "fmt"

func main() {
    x := make(map[string]int)

    x["A"] = 0
    x["B"] = 1

    val1, ok1 := x["C"]
    fmt.Println(val1)
    fmt.Println(ok1)

    val2, ok2 := x["A"]
    fmt.Println(val2)
    fmt.Println(ok2)
}

运行结果

在实际使用中,可以如下判断是否含有key值

package main

import "fmt"

func main() {
    x := make(map[string]int)

    x["A"] = 0
    x["B"] = 1

    if val, ok := x["C"]; ok {
        fmt.Println(val)
    }else{
        fmt.Println("不存在")
    }
}

输出如下

5)、map中使用delete函数删除元素,如果不存在,删除也不会报错,不会有任何其他影响

package main

import "fmt"

func main() {
    x := make(map[string]int)

    x["A"] = 0
    x["B"] = 1

    fmt.Println("Before Delete:")
    fmt.Println("Length:", len(x))
    fmt.Println(x)

    delete(x, "A")

    fmt.Println("After Delete:")
    fmt.Println("Length:", len(x))
    fmt.Println(x)
}

运行结果

6)、map复杂案例一:

package main

import "fmt"

func main() {
    var facebook = make(map[string]map[string]int)

    //注意下面对map赋值,不要对{}回车换行
    facebook["2018001"] = map[string]int{"Jemy": 25}
    facebook["2018002"] = map[string]int{"Andy": 23}
    facebook["2018003"] = map[string]int{"Bill": 22}

    for stu_no, stu_info := range facebook {
        fmt.Println("Student:", stu_no)
        for name, age := range stu_info {
            fmt.Println("Name:", name, "Age:", age)
        }
        fmt.Println()
    }
}

输出结果

map复杂案例二(执行结果和上面一样)

package main

import "fmt"

func main() {
    var facebook = map[string]map[string]int{
        "2018001": {"Jemy": 25},
        "2018002": {"Andy": 23},
        "2018003": {"Bill": 22},
    }

    for stu_no, stu_info := range facebook {
        fmt.Println("Student:", stu_no)
        for name, age := range stu_info {
            fmt.Println("Name:", name, "Age:", age)
        }
        fmt.Println()
    }
}

 

<think>我们正在讨论C#中的Dictionary(字典)类型。用户询问Dictionary是否支持切片操作。首先,需要明确“切片操作”通常指的是从序列中提取一部分连续的元素(如Python中的list切片)。在C#中,类似的概念可能是获取字典的一个子集。 根据引用[2]中提到:“注意: 1、与字符串一样,元组的元素不能修改。 2、元组也可以被索引切片,方法一样。... 5、string、list tuple 都属于 sequence(序列)。 Dictionary(字典)”。这里引用[2]将字典与序列(如字符串、列表、元组)区分开来,并指出序列支持索引切片,但字典并不属于序列。 因此,我们可以得出结论:C#的Dictionary本身并不直接支持切片操作,因为字典是无序的(在较新版本中,如.NET Core,Dictionary可能会记住添加顺序,但它并不是按索引访问的集合),它通过键值对存储数据,而不是通过索引。 但是,我们可以通过一些方法来实现类似“切片”的效果,例如: 1. 使用LINQ的SkipTake方法:这要求我们首先将字典转换为有序的序列(如按键或按值排序),然后跳过一定数量的元素并取一定数量的元素。 2. 或者根据键的范围来筛选(如果键是连续或有规律的话)。 示例代码(获取字典前n个元素): ```csharp using System.Linq; // 假设我们有一个字典 var dict = new Dictionary<int, string>(); // 添加一些元素... // 使用LINQ获取前10个元素(注意:字典默认无序,所以需要先排序,但排序依据需要根据需求确定) var slice = dict.OrderBy(kvp => kvp.Key).Take(10).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); ``` 然而,需要注意的是,这种“切片”并不是字典的原生操作,而是通过LINQ对字典的键值对序列进行处理。 另外,引用[1]中提到了一个精简的字典实现(DictionaryMini),虽然这并不直接相关,但说明字典的内部结构是哈希表,它不支持像数组那样的连续内存访问,因此无法进行真正的切片。 所以,回答用户的问题:C#的Dictionary本身不支持切片操作,但可以通过LINQ等方法模拟类似效果。 下面我们给出一个完整的回答。</think>在 C# 中,**Dictionary<TKey, TValue> 本身不支持直接的切片操作**,因为字典是基于哈希表的键值对集合,其元素存储是无序的(尽管在 .NET Core+ 中会记录插入顺序)。不过可以通过以下方法实现类似切片的功能: ### 替代方案:使用 LINQ 实现字典"切片" ```csharp using System.Linq; var dict = new Dictionary<int, string> { { 1, "Apple" }, { 2, "Banana" }, { 3, "Cherry" }, { 4, "Date" } }; // 获取第2-3个键值对(基于插入顺序) var slice = dict .Skip(1) // 跳过前1个 .Take(2) // 取2个 .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); // 结果:{ {2, "Banana"}, {3, "Cherry"} } ``` ### 关键限制说明 1. **顺序依赖**: - 在 .NET Framework 中,字典遍历顺序不确定 - 在 .NET Core 3.0+ 中,遍历顺序=插入顺序[^1] ```csharp // .NET Core+ 中保持插入顺序 foreach (var item in dict) // 顺序: 1→2→3→4 ``` 2. **性能考虑**: - `Skip()` + `Take()` 的时间复杂度为 $O(n)$ - 不适合超大字典(推荐分块加载[^4]) 3. **键值过滤替代方案**: ```csharp // 按键范围"切片"(要求键有序) var keySlice = dict.Where(kvp => kvp.Key >= 2 && kvp.Key <= 3) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); ``` ### 实际应用场景 1. **分页处理**: ```csharp int pageSize = 10; int page = 3; var pageData = dict.Skip((page - 1) * pageSize) .Take(pageSize) .ToDictionary(...); ``` 2. **数据流处理**: ```csharp // 结合流处理技术[^3] var window = dict.AsEnumerable() .SlidingWindow(size: 5) // 自定义窗口函数 .Last(); // 获取最近5个 ``` ### 推荐替代类型 如需原生切片支持,考虑: 1. `List<KeyValuePair<TKey,TValue>>` 2. `SortedDictionary<TKey,TValue>` 3. 自定义有序字典(参考 [DictionaryMini][^1]) > 总结:虽然字典没有内置切片,但通过 LINQ + 顺序控制可安全实现类似功能,在 .NET Core 3.0+ 环境下效果最佳[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值