golang中判断两个slice是否相等
在golang中我们可以轻松地通过==来判断两个数组(array)是否相等,但遗憾的是slice并没有相关的运算符,当需要判断两个slice是否相等时我们只能另寻捷径了。
slice相等的定义
我们选择最常见的需求,也就是当两个slice的类型和长度相同,且相等下标的值也是相等的,比如:
a := []int{1, 2, 3}
b := []int{1, 2, 3}
c := []int{1, 2}
d := []int{1, 3, 2}
上述代码中a和b是相等的,c因为长度和a不同所以不相等,d因为元素的排列顺序和a不同所以也不相等。
判断两个[]byte是否相等
为什么要单独将[]byte列举出来呢?
因为标准库提供了优化的比较方案,不再需要我们造轮子了:
package main
import (
"bytes"
"fmt"
)
func main() {
a := []byte{0, 1, 3, 2}
b := []byte{0, 1, 3, 2}
c := []byte{1, 1, 3, 2}
fmt.Println(bytes.Equal(a, b))
fmt.Println(bytes.Equal(a, c))
}
运行结果如下:

使用reflect判断slice是否相等
在判断类型不是[]byte的slice时,我们还可以借助reflect.DeepEqual,它用于深度比较两个对象包括它们内部包含的元素是否都相等:
func DeepEqual(x, y interface{}) bool
DeepEqual reports whether x and y are “deeply equal,” defined as follows. Two values of identical type are deeply equal if one of the following cases applies. Values of distinct types are never deeply equal.
...
Slice values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they point to the same initial entry of the same underlying array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.
这段话的意思不难理解,和我们在本文最开始时讨论的如何确定slice相等的原则是一样的,只不过它借助了一点运行时的“黑魔法”。
看例子:
package main
import (
"fmt"
"reflect"
)
func main() {
a := []int{1, 2, 3, 4}
b := []int{1, 3, 2, 4}
c := []int{1, 2, 3, 4}
fmt.Println(reflect.DeepEqual(a, b))
fmt.Println(reflect.DeepEqual(a, c))
}

手写判断
在golang中使用reflect通常需要付出性能代价,如果我们确定了slice的类型,那么自己实现slice的相等判断相对来说也不是那么麻烦:
func testEq(a, b []int) bool {
// If one is nil, the other must also be nil.
if (a == nil) != (b == nil) {
return false;
}
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
测试代码:
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4}
b := []int{1, 3, 2, 4}
c := []int{1, 2, 3, 4}
fmt.Println(testEq(a, b))
fmt.Println(testEq(a, c))
}
运行结果:

下面我们对后两种方案做个简单的性能测试,我们测试两个不相等但很相似的拥有20个元素的slice,这是在日常开发中较常见的情景:
func BenchmarkTestEq(b *testing.B) {
a := []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
c := []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = testEq(a, c)
}
}
func BenchmarkDeepEqual(b *testing.B) {
a := []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
c := []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = reflect.DeepEqual(a, c)
}
}
当然这个测试只能反应出有限的信息,正常情况下应该给出更全面的测试用例。不过在我们的演示中反射仍然付出了惊人的性能代价:

如果我们把slice的长度设为1000,那么差距就会更加明显:
func genDiffSlice(size int) ([]uint32, []uint32) {
a := make([]uint32, 0, size)
rand.Seed(time.Now().UnixNano())
for i := 0; i < size; i++ {
a = append(a, rand.Uint32())
}
b := make([]uint32, len(a))
copy(b, a)
b[len(b)-1] = rand.Uint32()
return a, b
}
func BenchmarkTestEq2(b *testing.B) {
a, c := genDiffSlice(1000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = testEq(a, c)
}
}
func BenchmarkDeepEqual2(b *testing.B) {
a, c := genDiffSlice(1000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = reflect.DeepEqual(a, c)
}
}

自己手写判断的性能更好,但是有个显而易见的弊端,当我们有多种类型的slice时我们就不得不编写不同版本的testEq,而它们唯一的不同仅仅只有slice的类型。
不过等到go2的泛型可以使用的时候,这样的弊端也就不复存在了,现在我们需要的是在代码的复杂度和运行性能上做出权衡。
参考
Checking the equality of two slices
golang中判断两个slice是否相等的更多相关文章
- Java与JavaScript中判断两字符串是否相等的区别
JavaScript是一种常用的脚本语言,这也决定了其相对于其他编程语言显得并不是很规范.在JavaScript中判断两字符串是否相等 直接用==,这与C++里的String类一样.而Java里的等号 ...
- java中判断两个字符串是否相等的问题
我最近刚学java,今天编程的时候就遇到一个棘手的问题,就是关于判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写.在java中,用的是eq ...
- python中判断两个对象是否相等
#coding=utf-8#比较两个对象是否相等#python 2中使用cmp(),==,is#is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false. ...
- Java中判断两个Long类型是否相等
在项目中将两个long类型的值比较是否相等,结果却遇到了疑问? 下面就陪大家看看一个神奇的现象! 1.1问题?为什么同样的类型,同样的值,却不相等呢? 1.2那么我们就需要探索一下源码了 源码中显示, ...
- Java中判断两个列表是否相等
CollectionUtils.isEqualCollection(final Collection a, final Collection b) CollectionUtils工具类中有一个查看两个 ...
- 【GoLang】golang中可以直接返回slice吗?YES
结论: 可以,slice本质是结构体,返回slice时返回的是结构体的值,结构体的指针.len.cap等信息也全部返回了. 如下: type slice struct { start *uintptr ...
- Java 中判断两个对象是否相等
由于每次实例化一个对象时,系统会分配一块内存地址给这个对象,而系统默认是根据内存地址来检测是否是同一个对象,所以就算是同一个类里实例化出来的对象它们也不会相等. public class Transp ...
- java中判断两个对象是否相等
package ceshi.com.job; import java.util.ArrayList; import java.util.Arrays; import java.util.List; p ...
- [置顶] 如何判断两个IP大小关系及是否在同一个网段中
功能点 判断某个IP地址是否合法 判断两个IP地址是否在同一个网段中 判断两个IP地址的大小关系 知识准备 IP协议 子网掩码 Java 正则表达式 基本原理 IP地址范围 0.0.0.0- 255 ...
随机推荐
- 微信小程序小Demo
微信小程序小Demo 调用API,轮播图,排行榜,底部BabTar的使用... board // board/board.js Page({ /** * 页面的初始数据 */ // 可以是网络路径图片 ...
- POS时机未到,POW强攻是实现全球货币的正确道路
POS时机未到,POW强攻是实现全球货币的正确道路 取代现今的货币体系的正确进攻方式是POW强攻,现在的货币是由力量背书的,以后的货币也是由力量背书的,只有因造币耗费的力量超过了所有其它力量的时候才能 ...
- C#循环结构
一.背景: 因编程的基础差,因此最近开始巩固学习C#基础,后期把自己学习的东西,总结相应文章中,有不足处请大家多多指教. 二.简介 有的时候,可能需要多次执行同一块代码.一般情况下,语句是顺序执行的: ...
- 利用Java EE里jsp制作登录界面
jsp连接数据库.百度经验. 1.在新建的Project中右键新建Floder 2.创建名为lib的包 3.创建完毕之后的工程目录 4.接下来解压你下载的mysql的jar包,拷贝其中的.jar文件 ...
- Java生鲜电商平台-商品的spu和sku数据结构设计与架构
Java生鲜电商平台-商品的spu和sku数据结构设计与架构 1. 先说明几个概念. 电商网站采用在商品模块,常采用spu+sku的数据结构算法,这种算法可以将商品的属性和商品的基本信息分离,分开维护 ...
- 十:装饰器模式(io流)
定义:装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 这一个解释,引自百度百科,我们 ...
- HTML input属性详谈
value属性 value属性指定输入字段的初始值: <form> 名字:<br> <input type="text" name="you ...
- AJAX小示例
一. 基本内容 定义:AJAX(Asynchronous Javascript And XML)翻译成中文就是"异步的Javascript和XML",即使用Javascript语言 ...
- Arthas实践--抽丝剥茧排查线上应用日志打满问题
现象 在应用的 service_stdout.log里一直输出下面的日志,直接把磁盘打满了: 23:07:34.441 [TAIRCLIENT-1-thread-1] DEBUG io.netty.c ...
- JVM源码分析之MetaspaceSize和MaxMetaspaceSize的区别
JVM加载类的时候,需要记录类的元数据,这些数据会保存在一个单独的内存区域内,在Java 7里,这个空间被称为永久代(Permgen),在Java 8里,使用元空间(Metaspace)代替了永久代. ...