从结构体中生成唯一哈希值

14
我想从一个结构体数组生成一个唯一的哈希值。顺序可能不同,但值是相同的。
例子:
type MyStruct struct {
   ID string
   Parameters map[string]interface{}
}

arr:= []MyStruct{MyStruct{ID: "test", Parameters: map[string]interface{}{"key": true, "key2": "value"} }, MyStruct{ID: "test2", Parameters: map[string]interface{}{"key2": "value", "key": true} }}
//The order is different even inside the Parameters fields

arr:= []MyStruct{MyStruct{ID: "test2", Parameters: map[string]interface{}{"key2": "value", "key": true} },MyStruct{ID: "test", Parameters: map[string]interface{}{"key": true, "key2": "value"} }}
在两种情况下,哈希值应该是相同的,因为结构体内部的值是相同的。
编辑:基本上想法是生成一个用于缓存的唯一哈希!我想要将每个结构体的独立哈希组合起来!

2
地图的设计是无序的。使用数组来保持顺序。 - user4466350
1
不太清楚你的问题所在。你是想对结构体的切片进行哈希处理,还是对每个结构体进行哈希处理?最好先问一下自己为什么要这么做。你是想检查内容是否相等(无论顺序如何),还是想进行校验和?请澄清一下。 - Marc
基本上的想法是生成一个用于缓存的唯一哈希值!我想要将每个结构体的独立哈希值组合起来! - Marco Talento
2
所以你想要一个包含无序数据结构的复杂数据结构的哈希值。实现这个最快的方法是对你的数据进行序列化(字段排序),并使用足够大的哈希函数来避免冲突。然而,我严重怀疑你的缓存需求需要整个数据,通常只需要一个非常有限的子集(例如:某人查询唯一ID并返回大量数据。这意味着你的缓存键是唯一ID,而不是数据)。 - Marc
4个回答

14

Feng说得对,被接受的答案不起作用,不仅因为结构中没有导出字段,而且因为MD5哈希的方式确实具有顺序的重要性,请参见RFC 1321 3.4

不确定有效性,但我已经通过将切片作为字节使用encoding/gob传递,并使用bytes表示要在Compare中使用的哈希来使其工作。

Playground

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type S struct {
    K1 string
    K2 int
}

func main() {
    sa := []S{{ K1: "foo", K2: 1}, {K1: "bar", K2: 2}, {K1: "baz", K2: 3,}}
    sb := []S{{ K1: "baz", K2: 3}, {K1: "bar", K2: 2}, {K1: "foo", K2: 1,}}
    sc := []S{}

    a := Hash(sa)
    b := Hash(sb)
    c := Hash(sc)

    fmt.Println(Compare(a, b))
    fmt.Println(Compare(a, c))
}

func Compare(a, b []byte) bool {
    a = append(a, b...)
    c := 0
    for _, x := range a {
        c ^= int(x)
    }
    return c == 0
}

func Hash(s []S) []byte {
    var b bytes.Buffer
    gob.NewEncoder(&b).Encode(s)
    return b.Bytes()
}

11

5
请阅读https://stackoverflow.com/help/how-to-answer和https://stackoverflow.com/help/deleted-answers,并改进你的回答。仅提供链接不是有效的回答。请注意不改变原意,使翻译更加通俗易懂。 - jasie

1

您可以使用https://github.com/rxwycdh/rxhash/tree/master方法HashStruct,对复杂嵌套结构进行计算哈希,以确保数据一致性。例如:

import "github.com/rxwycdh/rxhash"
type A struct {
  Values []int
}

a1 := A{Values: []int{1,2,3}}
a2 := A{Values: []int{3,2,1}} // unorder but content is equal a1

fmt.Println(HashStruct(a) == HashStruct(b)) // true

这对于检测结构体的内容是否发生了变化非常有用。


-3

您可以通过将数组序列化为字节数组,然后计算字节数组的md5哈希值来实现。由于md5哈希值是字节的哈希总和,因此顺序不重要。

要对数组的每个元素进行序列化,您可以使用json.Marshal,它适用于任何类型的struct

哈希函数将类似于以下内容:

func Hash(arr []SomeStruct) [16]byte {
    arrBytes := []byte{}
    for _, item := range arr {
        jsonBytes, _ := json.Marshal(item)
        arrBytes = append(arrBytes, jsonBytes...)
    }
    return md5.Sum(arrBytes)
}

请运行此工作示例程序here
func main() {
    arr1 := []SomeStruct{
        {
            param1: "abc",
            param2: 3,
        },
        {
            param1: "def",
            param2: 5,
        }, {
            param1: "deg",
            param2: 0,
        },
    }
    arr2 := []SomeStruct{
    {
            param1: "deg",
            param2: 0,
        },
        {
            param1: "def",
            param2: 5,
        },
        {
            param1: "abc",
            param2: 3,
        },
    }

    fmt.Printf("Hash1: %x\n", Hash(arr1))
    fmt.Printf("Hash2: %x\n", Hash(arr2))

}

func Hash(arr []SomeStruct) [16]byte {
    arrBytes := []byte{}
    for _, item := range arr {
        jsonBytes, _ := json.Marshal(item)
        arrBytes = append(arrBytes, jsonBytes...)
    }
    return md5.Sum(arrBytes)
}

这将输出:

Hash1: d065fee603fdcf75115204ec65310e1c
Hash2: d065fee603fdcf75115204ec65310e1c

2
太好了,我正在寻找这个:“由于md5校验和是字节的哈希和,所以顺序并不重要。”也许我需要开始阅读更多关于哈希算法的内容 :) - Marco Talento
2
由于SomeStruct中没有导出的字段,因此封送结果是相同的。如果您将param1和param2更改为大写,哈希结果将不同。请参见https://play.golang.org/p/J9mJqSnCvDC。 - Feng Yu
1
如前所述,这个答案是完全错误的。要使这样的东西起作用,数据需要按相同顺序排列,因此您需要先对结构进行排序。 - Pezo
为了进一步证明这是多么错误,请在调试器中运行它并查看 jsonBytes 是否等于“{}” 。您还可以使用print语句来输出 jsonBytes。 @MarcoTalento请取消接受此答案。 - Jeff
有人能确认JSON marshaller/encoder保证是确定性的吗,即使在不同版本之间?即使在未来的Go版本中,结果哈希值也会保持一致吗? - cutsoy
很不幸,当结构体中的一个字段是映射时,这会导致错误。json.Marshal在处理映射时是不确定的。 - Connor Finn McKelvey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接