独家封印解开: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 语言中的列表实现和应用。如果有任何问题,非常欢迎您提出讨论。


    目录
    相关文章
    |
    5月前
    |
    存储 Java 索引
    (Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
    字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
    253 4
    |
    5月前
    |
    存储 安全 Java
    【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
    结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
    311 2
    |
    7月前
    |
    Cloud Native 安全 Java
    Go:为云原生而生的高效语言
    Go:为云原生而生的高效语言
    464 1
    |
    7月前
    |
    Cloud Native Go API
    Go:为云原生而生的高效语言
    Go:为云原生而生的高效语言
    519 0
    |
    7月前
    |
    Cloud Native Java Go
    Go:为云原生而生的高效语言
    Go:为云原生而生的高效语言
    345 0
    |
    7月前
    |
    Cloud Native Java 中间件
    Go:为云原生而生的高效语言
    Go:为云原生而生的高效语言
    395 0
    |
    7月前
    |
    Cloud Native Java Go
    Go:为云原生而生的高效语言
    Go:为云原生而生的高效语言
    428 0
    |
    7月前
    |
    数据采集 Go API
    Go语言实战案例:多协程并发下载网页内容
    本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
    |
    7月前
    |
    数据采集 JSON Go
    Go语言实战案例:实现HTTP客户端请求并解析响应
    本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
    |
    8月前
    |
    JSON 前端开发 Go
    Go语言实战:创建一个简单的 HTTP 服务器
    本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。

    热门文章

    最新文章