详解Golang如何比较两个slice是否相等

目录
  • 前言
  • 判断两个[]byte是否相等
  • 使用reflect判断slice是否相等
  • 手写循环遍历比较
  • 性能比较
  • 总结

前言

开发中经常会遇到需要比较两个slice包含的元素是否完全相等的情况,在golang中是不能够直接通过 == 来判断两个切片是否相等的,我们通常会通过两种方法去比较切片是否相等,这里通过几个示例来看一下这两种方法,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

判断两个[]byte是否相等

因为在bytes标准库中提供了[]byte的比较方法,所以我们不再需要重复造轮子了;示例如下:

package main
import (
	"bytes"
	"fmt"
)
func main() {
	a := []byte{1,2,3}
	b := []byte{1,2}
	c := []byte{1,2,3}
	fmt.Println(bytes.Equal(a,b))
	fmt.Println(bytes.Equal(a,c))
}

执行代码输出如下:

使用reflect判断slice是否相等

我们还可以借助reflect包的reflect.DeepEqual方法来比较两个切片是否相等。这个写法很简单:

// ReflectDeepEqual 直接使用 reflect 包的 reflect.DeepEqual 方法进行比较
func StringSliceReflectEqual(a, b []string) bool {
    return reflect.DeepEqual(a, b)
}

手写循环遍历比较

我们都知道Golang中reflect效率很低,使用reflect通常需要付出性能代价,如果我们确定了slice的类型,那么自己实现slice的相等判断相对来说也不是那么麻烦:

// LoopCompare 循环遍历比较
// 先比较两个数的长度是否相等
// 再循环遍历每一个元素进行比较
func LoopCompare(a, b []int) bool {
    if len(a) != len(b) {
        return false
    }
    //与reflect.DeepEqual的结果保持一致:[]int{} != []int(nil)
    if (a == nil) != (b == nil) {
        return false
    }
    for i, v := range a {
        if v != b[i] {
            return false
        }
    }
    return true
}

性能比较

借助Benchmark来简单的测试比较下二者的性能。

package test

import (
	"reflect"
	"testing"
)

var (
	testA = []int{1,3,5,9,11,13}
	testB = []int{1,3,5,9,11,13,17,23}
)

// BenchmarkReflectDeepEqual 测试 reflect.DeepEqual 效率
func BenchmarkReflectDeepEqual(b *testing.B) {
	for n := 0; n < b.N; n++ {
		ReflectDeepEqual(testA, testB)
	}
}

// BenchmarkLoopCompare 测试 循环比较 效率
func BenchmarkLoopCompare(b *testing.B) {
	for n := 0; n < b.N; n++ {
		LoopCompare(testA, testB)
	}
}

在测试文件所在目录执行go test -bench=.命令

Benchmark对比测试结果:

根据测试结果我们可以得到:

  • 使用reflect的方式,649 ns完成一次操作;
  • 使用循环遍历的方式,12.7 ns完成一次比较,效率对比十分明显。

原因在于reflect 接收任意类型的参数,方法内部要对类型做判断;循环遍历仅支持已知固定类型,效率自然要高些。所以就只能忍痛放弃reflect了。

总结

以上就是整理的golang 判断 两个slice 是否相等全部内容,我们可以借助reflect包的reflect.DeepEqual方法来比较两个切片是否相等,但是效率比较低,如果我们确定了slice的类型,我们可以手写遍历循环的方式来比较,相对效率比较高,希望文章能够帮你解决golang 判断 两个slice 是否相等所遇到的程序开发问题。

到此这篇关于详解Golang如何比较两个slice是否相等的文章就介绍到这了,更多相关Golang比较slice是否相等内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言--切片(Slice)详解

    目录 一.定义切片 1.声明一个未指定大小的数组来定义切片 2.使用make()函数来创建切片 二.切片是可索引的 1.len() 和 cap() 函数 三.切片截取 四.增加切片的容量 说明: Go 语言切片是对数组的抽象. Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大. 一.定义切片 注意:切片不需要说明长度 1.声明一个未指定大小

  • Go之集合slice的实现

    目录 Slice(切片) 基于数组生成切片 切片修改 切片声明 Append 切片元素循环 Slice(切片) 切片和数组类似,可以把它理解为动态数组.切片是基于数组实现的,它的底层就是一个数组.对数组任意分隔,就可以得到一个切片.现在我们通过一个例子来更好地理解它,同样还是基于前面的 array. 基于数组生成切片 下面代码中的 array[2:5] 就是获取一个切片的操作,它包含从数组 array 的索引 2 开始到索引 5 结束的元素: array:=[5]string{"a",

  • Go基础系列:Go切片(分片)slice详解

    slice表示切片(分片),例如对一个数组进行切片,取出数组中的一部分值.在现代编程语言中,slice(切片)几乎成为一种必备特性,它可以从一个数组(列表)中取出任意长度的子数组(列表),为操作数据结构带来非常大的便利性,如python.perl等都支持对数组的slice操作,甚至perl还支持对hash数据结构的slice. 但Go中的slice和这些语言的slice不太一样,前面所说的语言中,slice是一种切片的操作,切片后返回一个新的数据对象.而Go中的slice不仅仅是一种切片动作,还

  • go 判断两个 slice/struct/map 是否相等的实例

    可以通过 reflect.DeepEqual 比较两个 slice/struct/map 是否相等: package main import ( "fmt" "reflect" ) type A struct { s string } func main() { a1 := A{s: "abc"} a2 := A{s: "abc"} if reflect.DeepEqual(a1, a2) { fmt.Println(a1,

  • 详解Golang如何比较两个slice是否相等

    目录 前言 判断两个[]byte是否相等 使用reflect判断slice是否相等 手写循环遍历比较 性能比较 总结 前言 开发中经常会遇到需要比较两个slice包含的元素是否完全相等的情况,在golang中是不能够直接通过 == 来判断两个切片是否相等的,我们通常会通过两种方法去比较切片是否相等,这里通过几个示例来看一下这两种方法,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助. 判断两个[]byte是否相等 因为在bytes标准库中提供了[]byte的比较方法,所以我们不再需要重复造轮子

  • 详解Golang语言中的interface

    interface是一组method签名的组合,interface可以被任意对象实现,一个对象也可以实现多个interface.任意类型都实现了空interface(也就是包含0个method的interface),空interface可以存储任意类型的值.interface定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口. go version go1.12 package main import ( "fmt" ) // 定义struct type Hu

  • 一文详解Golang中的切片数据类型

    目录 含义 定义 三个要素 切片与数组的区别 示例代码 切片内存分布 切片定义分类 数组生成切片 示例代码 切片索引 直接声明切片 定义语法 代码示例 使用make定义切片 常用操作 长度计算 容量计算 判断是否为空 切片追加 语法格式 尾部追加 开始位置追加 中间位置追加 复制 引用和复制 切片的删除 删除开头 删除中间 删除结尾 指定位置 排序 迭代器 含义 切片是一个种特殊的数组.是对数组的一个连续片段的引用,所以切片是一个引用类型.切片可以是数组中的一部分,也可以是由起始和终止索引标识的

  • 详解Golang 中的并发限制与超时控制

    前言 上回在 用 Go 写一个轻量级的 ssh 批量操作工具里提及过,我们做 Golang 并发的时候要对并发进行限制,对 goroutine 的执行要有超时控制.那会没有细说,这里展开讨论一下. 以下示例代码全部可以直接在 The Go Playground上运行测试: 并发 我们先来跑一个简单的并发看看 package main import ( "fmt" "time" ) func run(task_id, sleeptime int, ch chan st

  • 详解Golang开启http服务的三种方式

    前言 都说go标准库实用,Api设计简洁.这次就用go 标准库中的net/http包实现一个简洁的http web服务器,包括三种版本. v1最简单版 直接使用http.HandleFunc(partern,function(http.ResponseWriter,*http.Request){}) HandleFunc接受两个参数,第一个为路由地址,第二个为处理方法. //v1 func main() { http.HandleFunc("/", func(w http.Respon

  • 详解Golang中的各种时间操作

    需求 时间格式的转换比较麻烦,自己写了个工具,可以通过工具中的这些方法相互调用转成自己想要的格式,代码如下,后续有新的函数再添加 实现代码 package utils import "time" const ( TIMEFORMAT = "20060102150405" NORMALTIMEFORMAT = "2006-01-02 15:04:05" ) // 当前时间 func GetTime() time.Time{ return time.

  • 详解golang开发中http请求redirect的问题

    这两天在开发项目的时候遇到了一个问题,请求了一个URL,它会302到另一个地址,本意上只是想检查这个URL是否会做3XX的redirect跳转,结果每次reqeust都会返回最后一跳的结果.后来就看了下源码,了解下请求跳转的机制 实现代码 看下实现的简单代码 func main() { client := &http.Client{} url := "http://www.qq.com" reqest, err := http.NewRequest("GET"

  • 详解Golang中Channel的用法

    如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制.一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息. 1 创建channel 每个channel都有一个特殊的类型,也就是channels可发送数据的类型.一个可以发送int类型数据 的channel一般写为chan int.使用内置的make函数,如果第二个参数大于0,则表示创建一个带缓存的channel. ch := make(chan in

  • 详解Golang并发操作中常见的死锁情形

    目录 第一种情形:无缓存能力的管道,自己写完自己读 第二种情形:协程来晚了 第三种情形:管道读写时,相互要求对方先读/写 第四种情形:读写锁相互阻塞,形成隐形死锁 什么是死锁,在Go的协程里面死锁通常就是永久阻塞了,你拿着我的东西,要我先给你然后再给我,我拿着你的东西又让你先给我,不然就不给你.我俩都这么想,这事就解决不了了. 第一种情形:无缓存能力的管道,自己写完自己读 先上代码: func main() { ch := make(chan int, 0) ​ ch <- 666 x := <

  • 详解Golang五种原子性操作的用法

    目录 Go 语言提供了哪些原子操作 互斥锁跟原子操作的区别 比较并交换 atomic.Value保证任意值的读写安全 总结 本文我们详细聊一下Go语言的原子操作的用法,啥是原子操作呢?顾名思义,原子操作就是具备原子性的操作... 是不是感觉说了跟没说一样,原子性的解释如下: 一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性(atomicity) .这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态. CPU执行一系列操作时不可

随机推荐