Google Guava Hash(散列)

       Guava Hash(散列)指的是通过某种算法把数据源通过一系列的转换生成一串字符串。常见的例如hash code生成,加密字符的生成,检验字符的生成等等。接下来我们就对Guava Hash(散列)的使用做一个介绍。使用很简单。Guava Hash(散列)里面也给我们提供了很多hash算法。已经能满足我们大部分需求了。

一 Hash(散列)介绍

       Hash里面比较重要的类有:Hashing、HashFunction、Hasher、HashCode、Funnel、PrimitiveSink。

1.1 Hashing

       Hashing类是一个帮助类,里面提供的方法都是根据不同的hash算法生成对应的HashFunction对象。每个hash算法都实现了HashFunction。Hashing类里面提供的方法如下。

  • [ goodFastHash(int minimumBits) ]: 返回一个多用途的,临时使用的,非加密的 Hash Function
  • [ murmur3_32(int seed) ]: 使用指定参数值做种子返回一个 murmur3 算法实现的 32 位的哈希值
  • [ murmur3_32() ]: 使用零值种子返回一个 murmur3 算法实现的 32 位的哈希值
  • [ murmur3_128() ]: 使用零值种子返回一个 murmur3 算法实现的128位的哈希值。
  • [ sipHash24() ]: sipHash24 加密算法
  • [ sipHash24(long k0, long k1) ]: sipHash24 加密算法
  • [ md5() ]: md5 加密算法。
  • [ sha1() ]:sha1算法,hash。
  • [ sha256() ]: sha256 加密算法
  • [ sha384() ]: sha384 加密算法
  • [ sha512() ]: sha512 加密算法
  • [ hmacMd5(Key key) ]: hmacMd5 加密算法
  • [ hmacMd5(byte[] key) ]:hmacMd5 加密算法
  • [ hmacSha1(Key key) ]:hmacSha1 加密算法
  • [ hmacSha1(byte[] key) ]:hmacSha1 加密算法
  • [ hmacSha256(Key key) ]:hmacSha256 加密算法
  • [ hmacSha256(byte[] key) ]:hmacSha256 加密算法
  • [ hmacSha512(Key key) ]:hmacSha512 加密算法
  • [ hmacSha512(byte[] key) ]:hmacSha512 加密算法
  • [ crc32c() ]:CRC32C 校验算法
  • [ crc32() ]:CRC-32 校验算法
  • [ adler32() ]:Adler-32 校验算法
  • [ farmHashFingerprint64() ]
  • [ consistentHash(HashCode hashCode, int buckets) ]:以给定 buckets 的大小分配一致性哈希,最大限度的减少随 buckets 增长而进行重新映射的需要。
  • [ consistentHash(long input, int buckets) ]
  • [ combineOrdered(Iterable<HashCode> hashCodes) ]:以有序的方式使 HashCode 结合起来,如果两个 HashCode 集合采用此种方法结合后的 HashCode 相同,那么这两个 HashCode 集合中的元素可能是顺序相等的。
  • [ combineUnordered(Iterable<HashCode> hashCodes) ]: 以无序的方式使 HashCode 结合起来,如果两个 HashCode 集合采用此方法结合后的 HashCode 相同,那么这两个 HashCode 集合中的元素可能在某种排序方式下是顺序相等的。
  • [ concatenating(HashFunction first, HashFunction second, HashFunction... rest) ]: 将多个hash值拼接在一起。比如你想要1024位的一个hash,你就可以Hashing.concatenating(Hashing.sha512(), Hashing.sha512())

1.2 HashFunction

       HashFunction有两个作用:创建Hasher对象、也可以直接根据输入条件返回HashCode结果。HashFunction里面常用函数如下:

public interface HashFunction {


    /**
     * 获取一个Hasher
     */
    Hasher newHasher();
    Hasher newHasher(int expectedInputSize);

    /**
     * 根据input生成HashCode
     */
    HashCode hashInt(int input);
    HashCode hashLong(long input);
    HashCode hashBytes(byte[] input);
    HashCode hashBytes(byte[] input, int off, int len);
    HashCode hashBytes(ByteBuffer input);
    HashCode hashUnencodedChars(CharSequence input);
    HashCode hashString(CharSequence input, Charset charset);

    /**
     * 根据一个对象生成HashCode
     */
    <T> HashCode hashObject(T instance, Funnel<? super T> funnel);

    /**
     * 返回此哈希生成的每个哈希代码的位数(32的倍数)
     */
    int bits();

}

1.3 Hasher

       Hasher的主要作用是根据加入的数据源得到HashCode。数据源通过提供的putXxx()方法添加、hash()方法返回计算结果HashCode。

public interface Hasher extends PrimitiveSink {

    /**
     * 添加产生HashCode的数据源
     */
    @Override
    Hasher putByte(byte b);
    @Override
    Hasher putBytes(byte[] bytes);
    @Override
    Hasher putBytes(byte[] bytes, int off, int len);
    @Override
    Hasher putBytes(ByteBuffer bytes);
    @Override
    Hasher putShort(short s);
    @Override
    Hasher putInt(int i);
    @Override
    Hasher putLong(long l);
    @Override
    Hasher putFloat(float f);
    @Override
    Hasher putDouble(double d);
    @Override
    Hasher putBoolean(boolean b);
    @Override
    Hasher putChar(char c);
    @Override
    Hasher putUnencodedChars(CharSequence charSequence);
    @Override
    Hasher putString(CharSequence charSequence, Charset charset);

    /** 
     * 添加一个对象数据源
     */
    <T> Hasher putObject(T instance, Funnel<? super T> funnel);

    /**
     * 获取到HashCode
     */
    HashCode hash();

}

1.4 HashCode

       HashCode是对结果的一个封装。

public abstract class HashCode {
    
    /**
     * 返回此HashCode中的位数; 8的正数倍,注意哦是bit
     */
    public abstract int bits();

    /**
     * 把HashCode的前面四个字节转换为int(小端顺序排列)
     *
     * @throws IllegalStateException if {@code bits() < 32}
     */
    public abstract int asInt();

    /**
     * 把HashCode的前面八个字节转换为long(小端顺序排列)
     *
     * @throws IllegalStateException if {@code bits() < 64}
     */
    public abstract long asLong();

    /**
     * 如果HashCode位数足够多按照asLong()函数处理,不够直接转换为long
     */
    public abstract long padToLong();

    /**
     * HashCode以byte[]形式返回,字节数组的形式输出
     */
    // TODO(user): consider ByteString here, when that is available
    public abstract byte[] asBytes();

    /**
     * HashCode放到dest里面去,并且返回放入的返回长度
     */
    @CanIgnoreReturnValue
    public int writeBytesTo(byte[] dest, int offset, int maxLength);

    /**
     * 把int转换为32位长度的HashCode
     */
    public static HashCode fromInt(int hash);

    /**
     * 把long转换为64位长度的HashCode
     */
    public static HashCode fromLong(long hash);

    /**
     * 把byte数组转换为HashCode
     */
    public static HashCode fromBytes(byte[] bytes);
    
    /**
     * String转换为HashCode
     */
    public static HashCode fromString(String string);
    
    /**
     * 返回hash code对应的字符串
     */
    @Override
    public final String toString();

}

五 Funnel、PrimitiveSink

       定义了怎样将一个Object对象里面的各个属性放到PrimitiveSink里面,Hasher的putObject方法需要传入这样的对象。

Funnel

public interface Funnel<T> extends Serializable {

  /**
   * Sends a stream of data from the {@code from} object into the sink {@code into}. There is no
   * requirement that this data be complete enough to fully reconstitute the object later.
   *
   * @since 12.0 (in Guava 11.0, {@code PrimitiveSink} was named {@code Sink})
   */
  void funnel(T from, PrimitiveSink into);
}

PrimitiveSink

public interface PrimitiveSink {

    /**
     * 把参数放到PrimitiveSink里面去
     */
    PrimitiveSink putByte(byte b);
    PrimitiveSink putBytes(byte[] bytes);
    PrimitiveSink putBytes(byte[] bytes, int off, int len);
    PrimitiveSink putBytes(ByteBuffer bytes);
    PrimitiveSink putShort(short s);
    PrimitiveSink putInt(int i);
    PrimitiveSink putLong(long l);
    PrimitiveSink putFloat(float f);
    PrimitiveSink putDouble(double d);
    PrimitiveSink putBoolean(boolean b);
    PrimitiveSink putChar(char c);
    PrimitiveSink putUnencodedChars(CharSequence charSequence);
    PrimitiveSink putString(CharSequence charSequence, Charset charset);
    
}

       使用实例

Funnel<Person> personFunnel = new Funnel<Person>() {
    @Override
    public void funnel(Person person, PrimitiveSink into) {
        into
            .putInt(person.id)
            .putString(person.firstName, Charsets.UTF_8)
            .putString(person.lastName, Charsets.UTF_8)
            .putInt(birthYear);
    }
}

二 Hash(散列)使用实例

2.1 各种算法对应的hash code

        // 各种算法对应的hash code
        String input = "hello, world";
        // MD5
        System.out.println(Hashing.md5().hashBytes(input.getBytes()).toString());
        // sha256
        System.out.println(Hashing.sha256().hashBytes(input.getBytes()).toString());
        // sha512
        System.out.println(Hashing.sha512().hashBytes(input.getBytes()).toString());
        // crc32
        System.out.println(Hashing.crc32().hashBytes(input.getBytes()).toString());
        // MD5
        System.out.println(Hashing.md5().hashUnencodedChars(input).toString());

2.2 多个数据源对应的hash code

        HashFunction hf = Hashing.md5();
        HashCode hc = hf.newHasher()
                .putLong(10)
                .putString("abc", Charsets.UTF_8)
                .hash();
        System.out.println(hc.toString());

2.3 数据源是对象的情况

    @Test
    public void objectToHashCode() {

        // 需要hash的对象
        Person person = new Person(27, "wu", "xing", 1990);

        Funnel<Person> personFunnel = new Funnel<Person>() {
            @Override
            public void funnel(Person person, PrimitiveSink into) {
                into
                        .putInt(person.id)
                        .putString(person.firstName, Charsets.UTF_8)
                        .putString(person.lastName, Charsets.UTF_8)
                        .putInt(person.birthYear);
            }
        };

        // md5算法
        HashFunction hf = Hashing.md5();
        HashCode hc = hf.newHasher()
                .putString("abc", Charsets.UTF_8)
                .putObject(person, personFunnel)
                .hash();
        System.out.println(hc.toString());
    }

    class Person {
        int id;
        String firstName;
        String lastName;
        int birthYear;

        public Person(int id, String firstName, String lastName, int birthYear) {
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
            this.birthYear = birthYear;
        }
    }

       Guava Hash(散列)的使用很简单吧,首先在Hashing找到我们对应需要的hash算法(一般里面的这些hash算法能满足我们绝大部分的情况)。然后把数据源put进去,如果有多个数据源依次put进去就可以了。最后hash()就得到了我们最终hash的结果了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容