在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}

上述代码中ab是相等的,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是否相等的更多相关文章

  1. Java与JavaScript中判断两字符串是否相等的区别

    JavaScript是一种常用的脚本语言,这也决定了其相对于其他编程语言显得并不是很规范.在JavaScript中判断两字符串是否相等 直接用==,这与C++里的String类一样.而Java里的等号 ...

  2. java中判断两个字符串是否相等的问题

    我最近刚学java,今天编程的时候就遇到一个棘手的问题,就是关于判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写.在java中,用的是eq ...

  3. python中判断两个对象是否相等

    #coding=utf-8#比较两个对象是否相等#python 2中使用cmp(),==,is#is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false. ...

  4. Java中判断两个Long类型是否相等

    在项目中将两个long类型的值比较是否相等,结果却遇到了疑问? 下面就陪大家看看一个神奇的现象! 1.1问题?为什么同样的类型,同样的值,却不相等呢? 1.2那么我们就需要探索一下源码了 源码中显示, ...

  5. Java中判断两个列表是否相等

    CollectionUtils.isEqualCollection(final Collection a, final Collection b) CollectionUtils工具类中有一个查看两个 ...

  6. 【GoLang】golang中可以直接返回slice吗?YES

    结论: 可以,slice本质是结构体,返回slice时返回的是结构体的值,结构体的指针.len.cap等信息也全部返回了. 如下: type slice struct { start *uintptr ...

  7. Java 中判断两个对象是否相等

    由于每次实例化一个对象时,系统会分配一块内存地址给这个对象,而系统默认是根据内存地址来检测是否是同一个对象,所以就算是同一个类里实例化出来的对象它们也不会相等. public class Transp ...

  8. java中判断两个对象是否相等

    package ceshi.com.job; import java.util.ArrayList; import java.util.Arrays; import java.util.List; p ...

  9. [置顶] 如何判断两个IP大小关系及是否在同一个网段中

    功能点  判断某个IP地址是否合法 判断两个IP地址是否在同一个网段中 判断两个IP地址的大小关系 知识准备 IP协议 子网掩码 Java 正则表达式 基本原理 IP地址范围 0.0.0.0- 255 ...

随机推荐

  1. Docker Hub镜像加速器

    国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务. 1.配置加速地址 vim /etc/docker/daemo ...

  2. select 获取option中其他的属性的值

    <select name="tag_keys[]" id="category_type" required> <option value=&q ...

  3. C#中的一些对话框问题处理

    1. 对于打开文件对话框处理 #region 打开文件对话框 string StrPath; OpenFileDialog Flag = new OpenFileDialog(); Flag.Mult ...

  4. Python-- easy_install 的安装

    http://peak.telecommunity.com/dist/ez_setup.py 将这里面的复制出来打包成ez_setup.py 然后cmd到目录下,直接输入ez_setup.py 可能会 ...

  5. Mysql、Oracle、SQLServer等数据库参考文档免费分享下载

    场景 MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统 ...

  6. MES系统如何帮助烟草行业管理生产流程

    与很多其他行业一样,烟草MES系统可以帮助卷烟企业实现智能生产.精益制造.快速实现烟草企业数字化车间的创建,助力企业实现改造升级,从而提升企业生产效率,降低生产成产.烟草行业得MES者得天下. 烟草行 ...

  7. Qt背景不显示问题

    背景不显示的只有主窗口会发生,原因是主窗口使用的QWidget类 解决办法 重构paintEvent事件,添加即可 void LoginWidget::paintEvent(QPaintEvent * ...

  8. 什么是唯品会JIT业务

    以销定采的模式,供应商将商品发给唯品会仓库在由唯品会发给客户:首先在唯品会创建档期绑定PO此时设置的商品库存为虚拟库存,之后供应商根据实际产生的有效订单将订单中的商品发给唯品会,最后再由唯品会发给用户 ...

  9. Django 练习班级管理系统八 -- 上传文件

    Form表单上传文件 修改 views.py import os def upload(request): if request.method == 'GET': img_list = models. ...

  10. hydra使用,实例介绍

    hydra 是一个网络帐号破解工具,支持多种协议.其作者是van Hauser,David Maciejak与其共同维护.hydra在所有支持GCC的平台能很好的编译,包括Linux,所有版本的BSD ...