独家封印解开:Go语言列表List操作大揭秘

简介: 独家封印解开:Go语言列表List操作大揭秘

/ Go 语言列表 List 使用指南 /

Go 语言中并没有内置的列表 List 类型,但可以通过 slice、数组或链表实现列表功能。合理实现列表可以解决很多实际编码问题。

本文将全面介绍如何在 Go 语言中实现列表,内容涵盖

  1. 数组实现列表
  2. 切片实现列表
  3. 链表实现灵活列表
  4. 列表基本操作实现
  5. 线程安全列表
  6. 常见算法实现
  7. 列表与切片区别
  8. 应用场景


1

 

1. 数组实现列表

可以使用数组来实现简单的列表:

// 定义一个最大长度为100的int列表
var list [100]int
// 加入两个元素 
list[0] = 1  
list[1] = 2
// 读取第一个元素
v := list[0]
// 遍历数组列表
for i := 0; i < len(list); i++ {
  v := list[i]
  fmt.Println(v) 
}

数组实现的列表长度是固定的,删除元素后会留下空洞,无法动态缩容。

再来看一个例子:

// 定义一个最大长度为3的字符串列表
var strList [3]string 
// 尾部追加元素
strList[0] = "foo"
strList[1] = "bar"
// 头部插入元素
strList[2] = strList[1]
strList[1] = "baz"
// 读取第2个元素
v := strList[1]
// 遍历列表
for i := 0; i < len(strList); i++ {
  fmt.Println(strList[i]) 
}
// 删除第1个元素
strList[1] = ""

数组实现列表主要特点是简单直接,但是不够灵活。


2

 

2. 切片实现列表

切片实现的列表功能更加完整:

// 定义一个字符串切片
var strList []string
// 尾部追加元素  
strList = append(strList, "foo")
strList = append(strList, "bar")
// 读取第一个元素
v := strList[0] 
// 遍历切片列表
for i := range strList {
  fmt.Println(strList[i])
}
// 删除第1个元素
strList = append(strList[:1], strList[2:]...) 

切片列表可以动态增长,并且可以很方便地在尾部快速添加元素。

再来看一个例子:

// 定义一个数值切片
var numList []float64
// 尾部添加元素
numList = append(numList, 1.2)
// 头部添加元素 
numList = append([]float64{2.3}, numList...)
// 中间位置添加元素
numList = append(numList[:1], append([]float64{3.4}, numList[1:]...)...)
// 读取第2个元素
v := numList[1] 
// 删除第1个元素
numList = append(numList[:1], numList[2:]...)

切片允许很容易在头部或中间位置添加元素。


3

 

3. 链表实现灵活列表

可以通过自定义链表结构实现功能更加完备的列表:

// 链表节点
type Node struct {
  Value int
  Next *Node
}
// 初始化一个空链表 
var list List
// 尾部插入元素
node := &Node{Value: 1}
list.Append(node)
node = &Node{Value: 2}
list.Append(node)  
// 读取第1个节点
node := list.Head.Next 
// 遍历链表
for n := list.Head; n != nil; n = n.Next {
  fmt.Println(n.Value)
}
// 删除第2个节点
list.Remove(node.Next) 

链表需要自己实现相关的插入、删除、遍历逻辑,但是可以非常灵活地管理元素。

再举一个详细例子:

// 定义链表节点
type Node struct {
  Value int
  Next *Node
}
// 初始化链表
list := new(List)
// 头插法建立链表 
header := &Node{}
list.Append(header)
p := header
for i := 1; i <= 5; i++ {
  tmp := &Node{Value: i}
  p.Next = tmp
  p = tmp
}
// 尾插法插入元素 
tail := &Node{Value: 6}
p.Next = tail
// 删除第2个节点
list.Remove(header.Next.Next)
// 查找节点
n := list.Find(2)
// 反转链表
list.Reverse(header)
// 遍历链表
for n := header.Next; n != nil; n = n.Next {
  fmt.Println(n.Value)  
}

链表需要自己实现各种算法,但是可以非常灵活。


4

 

4. 列表基本操作实现

下面实现列表的一些基本操作:

尾部添加元素

对数组或切片使用 append 直接追加:

list = append(list, v)

对链表可以用尾插法:

node := &Node{Value: v}
tail.Next = node 
tail = node

头部添加元素

对切片可使用 append 向前追加:

list = append([]int{v}, list...)

对链表可用头插法:

node := &Node{Value: v}
node.Next = head.Next
head.Next = node

中间位置添加元素

对切片,需要创建新切片并复制元素:

list = append(list[:i], append([]int{v}, list[i:]...)...)

对链表直接修改指针完成插入:

node := &Node{Value:v}
node.Next = p.Next 
p.Next = node

读取元素

数组和切片直接索引定位:

v := list[i]

    删除元素

    数组切片创建新切片完成删除:

    list = append(list[:i], list[i+1:]...)

    链表修改指针完成删除:

    p.Next = p.Next.Next


    5

     

    5. 线程安全列表

    上述列表都不是线程安全的,可以使用同步原语实现线程安全:

    import "sync"
    // 定义一个互斥锁
    var mutex sync.Mutex
    // 加锁保护列表
    mutex.Lock() 
    list = append(list, v)
    mutex.Unlock()
    // 使用读写锁分别保护读写
    var rw sync.RWMutex
    rw.RLock()
    v = list[0]  
    rw.RUnlock()
    rw.Lock()
    list = append(list, v)  
    rw.Unlock()

    加锁可以实现线程安全,但是需要权衡锁带来的性能损耗。


    6

     

    6. 常见算法实现

    列表可以实现各种常见算法:

    排序

    切片列表可以使用 sort 包排序:

    sort.Ints(list)

    链表可以实现 sort 接口排序:

    sort.Sort(list)

    查找

    顺序查找:

    for i := 0; i < len(list); i++ {
      if list[i] == v {
        return i
      }
    }
    return -1 

    二分查找需要列表已排序:

    func binarySearch(list []int, v int) int {
      // 二分查找算法
      return index 
    }

    翻转

    数组可以复制反序写入:

    for i := 0; i < len(list); i++ {
      reversed[len(list)-i-1] = list[i] 
    }

    链表修改指针顺序:

    var prev *Node
    for cur := head.Next; cur != nil; {
      nxt := cur.Next
      cur.Next = prev
      prev = cur 
      cur = nxt
    }
    head.Next = prev

    7

     

    7. 列表与切片区别

    列表与切片的区别主要是:

    • 列表强调顺序性,切片可无序
    • 列表可以快速插入删除,切片更适合追加
    • 列表长度固定,切片动态大小


    8

     

    8. 应用场景

    列表常见使用场景包括:

    • 待办任务列表
    // 待办任务队列  
    var taskQueue []*Task 
    // 添加任务
    taskQueue = append(taskQueue, NewTask(params))
    // 获取执行任务  
    task := taskQueue[0] 
    taskQueue = taskQueue[1:]
    // 已完成任务列表
    var doneList []*Task

    商品列表

    // 商品列表
    var productList []*Product
    // 添加商品
    productList = append(productList, NewProduct(name, desc))
    // 删除下架商品
    for i := 0; i < len(productList); i++ {
      if !productList[i].OnSale {
        productList = append(productList[:i], productList[i+1:]...) 
      }
    }

    LRU 缓存淘汰

    // 双向链表
    type Node struct {
      Prev, Next *Node
      Key, Value interface{}
    }
    // Get时将节点移到表头  
    lru.Evict(node)
    lru.Add(node)
    // 淘汰链表尾节点
    tail := lru.tail 
    lru.Remove(tail)

    列表还可以用来实现栈、队列等数据结构。


    9

     

    总结

    本文全面介绍了 Go 语言实现高效列表的方法,包含数组、切片和链表实现,分析它们的特点,使用示例代码演示各种操作的实现,并对比了列表与切片的区别,以及举例列出了使用场景。希望可以帮助您深入理解 Go 语言中的列表实现和应用。如果有任何问题,非常欢迎您提出讨论。


    目录
    相关文章
    |
    8月前
    |
    编译器 Go
    揭秘 Go 语言中空结构体的强大用法
    Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
    |
    8月前
    |
    运维 监控 算法
    监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
    在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
    |
    2月前
    |
    数据采集 Go API
    Go语言实战案例:多协程并发下载网页内容
    本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
    |
    2月前
    |
    数据采集 JSON Go
    Go语言实战案例:实现HTTP客户端请求并解析响应
    本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
    |
    3月前
    |
    JSON 前端开发 Go
    Go语言实战:创建一个简单的 HTTP 服务器
    本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
    |
    3月前
    |
    Go
    如何在Go语言的HTTP请求中设置使用代理服务器
    当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
    251 0
    |
    4月前
    |
    JSON 编解码 API
    Go语言网络编程:使用 net/http 构建 RESTful API
    本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
    |
    5月前
    |
    分布式计算 Go C++
    初探Go语言RPC编程手法
    总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
    118 10
    |
    7月前
    |
    人工智能 Java
    Java 中数组Array和列表List的转换
    本文介绍了数组与列表之间的相互转换方法,主要包括三部分:1)使用`Collections.addAll()`方法将数组转为列表,适用于引用类型,效率较高;2)通过`new ArrayList&lt;&gt;()`构造器结合`Arrays.asList()`实现类似功能;3)利用JDK8的`Stream`流式计算,支持基本数据类型数组的转换。此外,还详细讲解了列表转数组的方法,如借助`Stream`实现不同类型数组间的转换,并附带代码示例与执行结果,帮助读者深入理解两种数据结构的互转技巧。
    363 1
    Java 中数组Array和列表List的转换
    |
    8月前
    |
    存储 缓存 安全
    Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
    `sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。