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


    目录
    相关文章
    |
    1天前
    |
    存储 NoSQL 算法
    Redis6入门到实战------ 三、常用五大数据类型(列表(List)、集合(Set)、哈希(Hash)、Zset(sorted set))
    这是关于Redis 6入门到实战的文章,具体内容涉及Redis的五大数据类型:列表(List)、集合(Set)、哈希(Hash)、有序集合(Zset(sorted set))。文章详细介绍了这些数据类型的特点、常用命令以及它们背后的数据结构。如果您有任何关于Redis的具体问题或需要进一步的帮助,请随时告诉我。
    |
    1天前
    |
    存储 JavaScript
    DOM 节点列表长度(Node List Length)
    名为 &quot;title&quot; 的元素节点,并存储在节点列表 x 中。通过 &quot;length&quot; 属性确定 x 的长度(即 &quot;title&quot; 节点总数)。利用 for 循环遍历整个列表,访问每个 &quot;title&quot; 节点的第一个子节点的值,并将其写入文档。
    |
    2天前
    |
    安全 Java Go
    探索Go语言在高并发环境中的优势
    在当今的技术环境中,高并发处理能力成为评估编程语言性能的关键因素之一。Go语言(Golang),作为Google开发的一种编程语言,以其独特的并发处理模型和高效的性能赢得了广泛关注。本文将深入探讨Go语言在高并发环境中的优势,尤其是其goroutine和channel机制如何简化并发编程,提升系统的响应速度和稳定性。通过具体的案例分析和性能对比,本文揭示了Go语言在实际应用中的高效性,并为开发者在选择合适技术栈时提供参考。
    |
    1天前
    |
    监控 NoSQL Go
    Go语言中高效使用Redis的Pipeline
    Redis 是构建高性能应用时常用的内存数据库,通过其 Pipeline 和 Watch 机制可批量执行命令并确保数据安全性。Pipeline 类似于超市购物一次性结账,减少网络交互时间,提升效率。Go 语言示例展示了如何使用 Pipeline 和 Pipelined 方法简化代码,并通过 TxPipeline 保证操作原子性。Watch 机制则通过监控键变化实现乐观锁,防止并发问题导致的数据不一致。这些机制简化了开发流程,提高了应用程序的性能和可靠性。
    6 0
    |
    3天前
    |
    存储 JavaScript
    DOM 节点列表长度(Node List Length)
    名为 &quot;title&quot; 的元素节点,并存储在节点列表 x 中。通过 &quot;length&quot; 属性确定 x 的长度(即 &quot;title&quot; 节点总数)。利用 for 循环遍历整个列表,访问每个 &quot;title&quot; 节点的第一个子节点的值,并将其写入文档。
    |
    4天前
    |
    NoSQL Go Redis
    Go语言中如何扫描Redis中大量的key
    在Redis中,遍历大量键时直接使用`KEYS`命令会导致性能瓶颈,因为它会一次性返回所有匹配的键,可能阻塞Redis并影响服务稳定性。为解决此问题,Redis提供了`SCAN`命令来分批迭代键,避免一次性加载过多数据。本文通过两个Go语言示例演示如何使用`SCAN`命令:第一个示例展示了基本的手动迭代方式;第二个示例则利用`Iterator`简化迭代过程。这两种方法均有效地避免了`KEYS`命令的性能问题,并提高了遍历Redis键的效率。
    15 0
    |
    5天前
    |
    监控 Serverless Go
    Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
    Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
    |
    5天前
    |
    存储 JavaScript
    DOM 节点列表长度(Node List Length)
    名为 &quot;title&quot; 的元素节点,并存储在节点列表 x 中。通过 &quot;length&quot; 属性确定 x 的长度(即 &quot;title&quot; 节点总数)。利用 for 循环遍历整个列表,访问每个 &quot;title&quot; 节点的第一个子节点的值,并将其写入文档。
    |
    2月前
    |
    安全 Java
    java线程之List集合并发安全问题及解决方案
    java线程之List集合并发安全问题及解决方案
    186 1
    |
    21天前
    |
    Java API Apache
    怎么在在 Java 中对List进行分区
    本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。