Guava优美代码-18-哈希

Guava哈希算法

Java内建的散列码(hash code)概念被限制为32位,并且没有分离散列算法和它们所作用的数据,因此很难用备选算法进行替换。此外,使用Java内建方法实现的散列码通常是劣质的,部分是因为它们最终都依赖于JDK类中已有的劣质散列码。

Object.hashCode往往很快,但是在预防碰撞上却很弱,也没有对分散性的预期。这使得它们很适合在散列表中运用,因为额外碰撞只会带来轻微的性能损失,同时差劲的分散性也可以容易地通过再散列来纠正(Java中所有合理的散列表都用了再散列方法)。然而,在简单散列表以外的散列运用中,Object.hashCode几乎总是达不到要求——因此,Guava为我们提供了补充的Hash工具类。

Guava哈希工具类

HashFunction

HashFunction是一个单纯的(引用透明的)、无状态的方法,它把任意的数据块映射到固定数目的位指,并且保证相同的输入一定产生相同的输出,不同的输入尽可能产生不同的输出。

Hasher

HashFunction的实例可以提供有状态的Hasher,Hasher提供了流畅的语法把数据添加到散列运算,然后获取散列值。Hasher可以接受所有原生类型、字节数组、字节数组的片段、字符序列、特定字符集的字符序列等等,或者任何给定了Funnel实现的对象。

Funnel

Funnel描述了如何把一个具体的对象类型分解为原生字段值,从而写入PrimitiveSink。

HashCode

一旦Hasher被赋予了所有输入,就可以通过hash()方法获取HashCode实例(多次调用hash()方法的结果是不确定的)。HashCode可以通过asInt()asLong()asBytes()方法来做相等性检测,此外,writeBytesTo(array, offset, maxLength)把散列值的前maxLength字节写入字节数组。

Hashing

Hashing类提供了若干哈希函数,以及运算HashCode对象的工具方法。

  • md5
  • sha256
  • sha512
  • sha1
  • murmur3_128
  • murmur3_32
  • goodFastHash

哈希工具类使用实例

防碰撞哈希

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
public void testMd5Hash() {
Student student1 = new Student(2012302580314l, 21, "Richard");
Student student2 = new Student(2012302580311l, 21, "Richard");
HashFunction hashFunction = Hashing.md5();
HashCode hashCod1 = hashFunction.newHasher()
.putObject(student1, (Funnel<Student>) (from, into) -> into
.putLong(from.sid)
.putInt(from.age)
.putString(from.name, Charsets.UTF_8)).hash();
HashCode hashCode2 = hashFunction.newHasher()
.putObject(student2, (Funnel<Student>) (from, into) -> into
.putLong(from.sid)
.putInt(from.age)
.putString(from.name, Charsets.UTF_8)).hash();
System.out.println(hashCod1.asInt());
System.out.println(hashCode2.asInt());
}

class Student {
final long sid;
final int age;
final String name;

Student(long sid, int age, String name) {
this.sid = sid;
this.age = age;
this.name = name;
}
}

一致性哈希

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testConsistentHash() {
List<String> ips = Lists.newArrayList("192.168.1.100",
"192.168.1.110", "192.168.1.120");
long ipHashCode1 = Hashing.md5().newHasher().putString(ips.get(0), Charsets.UTF_8).hash().asLong();
long ipHashCode2 = Hashing.md5().newHasher().putString(ips.get(1), Charsets.UTF_8).hash().asLong();
long ipHashCode3 = Hashing.md5().newHasher().putString(ips.get(2), Charsets.UTF_8).hash().asLong();

System.out.println("ip1: " + Hashing.consistentHash(ipHashCode1, 3));
System.out.println("ip2: " + Hashing.consistentHash(ipHashCode2, 3));
System.out.println("ip3: " + Hashing.consistentHash(ipHashCode3, 3));
}

上面两个例子,分别从防碰撞和分布性两个角度来说明哈希工具类的使用方法的。首先是防碰撞,第一个测试采用了md5方式,当然了,还可以使用其他的一些算法,不过md5算法是比较好的选择,这种方式能有效地保证各个对象的hash值不发生碰撞。第二种方式是一致性哈希算法,这种方式适用范围较广,一般用在分布式环境下,当请求过多的时候,一致性哈希能有效地将请求均匀地发送到不同的主机上执行,保证了很好的负载均衡。

总结

关于一致性哈希我建议读者到CSDN的这篇博文下好好了解一下,这篇文章的作者对一致性哈希有非常透彻的理解,能帮助大家很好地理解一致性哈希是什么以及它能做什么。

CSDN博文地址:http://blog.csdn.net/cywosp/article/details/23397179/

分享到