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 ...
随机推荐
- 博客中新浪图床 迁移至 阿里云的OSS
前言 因为之前有个新浪的图床,还挺好用,而且免费,自己博客的图片上传到其上面也挺方便的,但是,前几周吧,突然图片就不能访问了,之前本来是想通过添加 meta 头来解决的,但是发现没有效果.于是就自己搞 ...
- C++ 在线编译器(支持 C++11)
C++11 的 Inheriting constructors 特性在 GCC 4.8 以前的版本及 VS2013 中都没有支持,测试起来比较麻烦,所以搜集到了几个支持 GCC 4.8 及更高版本的在 ...
- layui + mvc + ajax 导出Excel功能
为了更方便,没基础的伙伴更容易理解,我尽量详细简便 省了很多代码,一步一步的试 自己引入文件 1. html 前端视图代码 Layui的数据绑定 全部代码 @{ Layout = null; } &l ...
- RabbitMQ实战应用技巧
1. RabbitMQ实战应用技巧 1.1. 前言 由于项目原因,之后会和RabbitMQ比较多的打交道,所以让我们来好好整理下RabbitMQ的应用实战技巧,尽量避免日后的采坑 1.2. 概述 Ra ...
- Java基础回顾——反射+案例
一,引言: 从大二刚开始接触java,了解其基本语法,到用swing编写图形界面的程序,到JavaWeb用SSH三大框架编写小网站,后面又学SSM,SpringBoot,SpringCloud等.可以 ...
- Scrum冲刺第二篇
一.每日例会 会议照片 成员 昨日已完成的工作 今日计划完成的工作 工作中遇到的困难 陈嘉欣 撰写博客,管理成员提交代码 每日博客,根据队员代码问题更改规范文档安排后续工作 队员提交的代码管理困难 邓 ...
- Hive:数据倾斜
数据倾斜问题 数据倾斜是大数据领域绕不开的拦路虎,当你所需处理的数据量到达了上亿甚至是千亿条的时候,数据倾斜将是横在你面前一道巨大的坎.很可能有几周甚至几月都要头疼于数据倾斜导致的各类诡异的问题. 数 ...
- 数据挖掘--K-means
K-Means方法是MacQueen1967年提出的.给定一个数据集合X和一个整数K(n),K-Means方法是将X分成K个聚类并使得在每个聚类中所有值与该聚类中心距离的总和最小. K-Means聚 ...
- 关于VS2015 发布.net mvc 网站失败的问题
问题:VS生成成功,发布失败,在“正在连接到***文件夹”处就不能继续了.. 项目开发告一段落,准备部署到服务器上进行最后测试,但是始终发布失败 生成成功,发布失败,没有任何提示信息 一开始以为是文 ...
- <人人都懂设计模式>-单例模式
这个模式,我还是了解的. 书上用了三种不同的方法. class Singleton1: # 单例实现方式1 __instance = None __is_first_init = False def ...