一 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();

}

1.5 Funnel

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

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);
}

1.6 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);

}

二 示例

1、各种算法对应的hash code:

String strInput = "hello, world";
int intInput = 35;
long longInput = 35L;

// MD5
HashFunction md5 = Hashing.md5();

//输入byte数组
HashCode hashBytes = md5.hashBytes(strInput.getBytes());
String string = hashBytes.toString();
System.out.println(string); //e4d7f1b4ed2e42d15898f4b27b019da4
System.out.println(hashBytes.asInt()); //-1259218972
System.out.println(hashBytes.asLong()); //-3368077972841834524
System.out.println(Arrays.toString(hashBytes.asBytes())); //[-28, -41, -15, -76, -19, 46, 66, -47, 88, -104, -12, -78, 123, 1, -99, -92]
long bytes2LongLittle = bytes2LongLittle(hashBytes.asBytes());
System.out.println(bytes2LongLittle); //-3368077972841834524

//输入string
HashCode hashString = md5.hashString(strInput, Charsets.UTF_8);
System.out.println(hashString.toString()); //e4d7f1b4ed2e42d15898f4b27b019da4
System.out.println(hashString.asInt()); //-1259218972
System.out.println(hashString.asLong()); //-3368077972841834524
System.out.println(Arrays.toString(hashString.asBytes())); //-3368077972841834524

//输入int
HashCode hashInt = md5.hashInt(intInput);
System.out.println(hashInt.toString()); //5c3c0aa3b52719bbdfddc762b7bc7e60

//输入long
HashCode hashLong = md5.hashLong(longInput);
System.out.println(hashLong.toString()); //9f883302611663a2993fc49042d1d20d


System.out.println("~~~~~~~~~~~~~~");
// sha256
System.out.println(Hashing.sha256().hashBytes(strInput.getBytes()).toString());
// sha512
System.out.println(Hashing.sha512().hashBytes(strInput.getBytes()).toString());
// crc32
System.out.println(Hashing.crc32().hashBytes(strInput.getBytes()).toString());

总结:

  1. 使用Hashing类的静态方法,创建不同的HashFunction实例;(md5、sha256、murmruhash);
  2. 根据输入类型,调用HashFunction实例的hashString、hashObject、hashInt、hashLong、hashBytes等方法,执行hash运算,返回HashCode实例;
  3. 调用HashCode实例的toString、asInt、asLong、asBytes方法,获得不同类型的哈希值。

说明:HashCode的asInt()和asLong()就是将bytes数组按照小端转成int和long

String strInput = "hello, world";

// MD5
HashFunction md5 = Hashing.md5();
HashCode hashBytes = md5.hashBytes(strInput.getBytes());
System.out.println(hashBytes.asLong()); //-3368077972841834524

System.out.println(bytes2LongLittle); //-3368077972841834524

private static long bytes2LongLittle(byte[] array) {
return ((((long) array[0] & 0xff))
| (((long) array[1] & 0xff) << 8)
| (((long) array[2] & 0xff) << 16)
| (((long) array[3] & 0xff) << 24)
| (((long) array[4] & 0xff) << 32)
| (((long) array[5] & 0xff) << 40)
| (((long) array[6] & 0xff) << 48)
| (((long) array[7] & 0xff) << 56));
}

2、多数据源对应的hash code:

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

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