diff --git a/CHANGELOG.md b/CHANGELOG.md
index 305e04681..1bc89ef85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
### 新特性
* 【core 】 增加逻辑,对于原始类型注入,使用默认值(issue#797@Github)
+* 【core 】 增加CityHash算法
### Bug修复
* 【core 】 修复NumberWordFormatter拼写错误(issue#799@Github)
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java
new file mode 100644
index 000000000..ac97abbc2
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/CityHash.java
@@ -0,0 +1,538 @@
+package cn.hutool.core.lang.hash;
+
+import java.util.Arrays;
+
+/**
+ * Google发布的Hash计算算法:CityHash64 与 CityHash128。
+ * 它们分别根据字串计算 64 和 128 位的散列值。这些算法不适用于加密,但适合用在散列表等处。
+ *
+ *
+ * 代码来自:https://github.com/rolandhe/string-tools
+ * 原始算法:https://github.com/google/cityhash
+ *
+ * @author hexiufeng
+ * @since 5.2.5
+ */
+public class CityHash {
+
+ // Some primes between 2^63 and 2^64 for various uses.
+ private static final long k0 = 0xc3a5c85c97cb3127L;
+ private static final long k1 = 0xb492b66fbe98f273L;
+ private static final long k2 = 0x9ae16a3b2f90404fL;
+ private static final long kMul = 0x9ddfea08eb382d69L;
+
+ // Magic numbers for 32-bit hashing. Copied from Murmur3.
+ private static final int c1 = 0xcc9e2d51;
+ private static final int c2 = 0x1b873593;
+
+
+ /**
+ * 计算32位City Hash值
+ *
+ * @param data 数据
+ * @return hash值
+ */
+ public static int hash32(byte[] data) {
+ int len = data.length;
+ if (len <= 24) {
+ return len <= 12 ?
+ (len <= 4 ? hash32Len0to4(data) : hash32Len5to12(data)) :
+ hash32Len13to24(data);
+ }
+
+ // len > 24
+ int h = len, g = c1 * len, f = g;
+ int a0 = rotate32(fetch32(data, len - 4) * c1, 17) * c2;
+ int a1 = rotate32(fetch32(data, len - 8) * c1, 17) * c2;
+ int a2 = rotate32(fetch32(data, len - 16) * c1, 17) * c2;
+ int a3 = rotate32(fetch32(data, len - 12) * c1, 17) * c2;
+ int a4 = rotate32(fetch32(data, len - 20) * c1, 17) * c2;
+ h ^= a0;
+ h = rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ h ^= a2;
+ h = rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a1;
+ g = rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ g ^= a3;
+ g = rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ f += a4;
+ f = rotate32(f, 19);
+ f = f * 5 + 0xe6546b64;
+ int iters = (len - 1) / 20;
+
+ int pos = 0;
+ do {
+ a0 = rotate32(fetch32(data, pos) * c1, 17) * c2;
+ a1 = fetch32(data, pos + 4);
+ a2 = rotate32(fetch32(data, pos + 8) * c1, 17) * c2;
+ a3 = rotate32(fetch32(data, pos + 12) * c1, 17) * c2;
+ a4 = fetch32(data, pos + 16);
+ h ^= a0;
+ h = rotate32(h, 18);
+ h = h * 5 + 0xe6546b64;
+ f += a1;
+ f = rotate32(f, 19);
+ f = f * c1;
+ g += a2;
+ g = rotate32(g, 18);
+ g = g * 5 + 0xe6546b64;
+ h ^= a3 + a1;
+ h = rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a4;
+ g = Integer.reverseBytes(g) * 5;
+ h += a4 * 5;
+ h = Integer.reverseBytes(h);
+ f += a0;
+ int swapValue = f;
+ f = g;
+ g = h;
+ h = swapValue;
+
+ pos += 20;
+ } while (--iters != 0);
+
+ g = rotate32(g, 11) * c1;
+ g = rotate32(g, 17) * c1;
+ f = rotate32(f, 11) * c1;
+ f = rotate32(f, 17) * c1;
+ h = rotate32(h + g, 19);
+ h = h * 5 + 0xe6546b64;
+ h = rotate32(h, 17) * c1;
+ h = rotate32(h + f, 19);
+ h = h * 5 + 0xe6546b64;
+ h = rotate32(h, 17) * c1;
+ return h;
+ }
+
+ /**
+ * 计算64位City Hash值
+ *
+ * @param data 数据
+ * @return hash值
+ */
+ public static long hash64(byte[] data) {
+ int len = data.length;
+ if (len <= 32) {
+ if (len <= 16) {
+ return hashLen0to16(data);
+ } else {
+ return hashLen17to32(data);
+ }
+ } else if (len <= 64) {
+ return hashLen33to64(data);
+ }
+
+ // For strings over 64 bytes we hash the end first, and then as we
+ // loop we keep 56 bytes of state: v, w, x, y, and z.
+ long x = fetch64(data, len - 40);
+ long y = fetch64(data, len - 16) + fetch64(data, len - 56);
+ long z = hashLen16(fetch64(data, len - 48) + len, fetch64(data, len - 24));
+ Number128 v = weakHashLen32WithSeeds(data, len - 64, len, z);
+ Number128 w = weakHashLen32WithSeeds(data, len - 32, y + k1, x);
+ x = x * k1 + fetch64(data, 0);
+
+ // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+ len = (len - 1) & ~63;
+ int pos = 0;
+ do {
+ x = rotate(x + y + v.getLowValue() + fetch64(data, pos + 8), 37) * k1;
+ y = rotate(y + v.getHighValue() + fetch64(data, pos + 48), 42) * k1;
+ x ^= w.getHighValue();
+ y += v.getLowValue() + fetch64(data, pos + 40);
+ z = rotate(z + w.getLowValue(), 33) * k1;
+ v = weakHashLen32WithSeeds(data, pos, v.getHighValue() * k1, x + w.getLowValue());
+ w = weakHashLen32WithSeeds(data, pos + 32, z + w.getHighValue(), y + fetch64(data, pos + 16));
+ // swap z,x value
+ long swapValue = x;
+ x = z;
+ z = swapValue;
+ pos += 64;
+ len -= 64;
+ } while (len != 0);
+ return hashLen16(hashLen16(v.getLowValue(), w.getLowValue()) + shiftMix(y) * k1 + z,
+ hashLen16(v.getHighValue(), w.getHighValue()) + x);
+ }
+
+ /**
+ * 计算64位City Hash值
+ *
+ * @param data 数据
+ * @param seed0 种子1
+ * @param seed1 种子2
+ * @return hash值
+ */
+ public static long hash64(byte[] data, long seed0, long seed1) {
+ return hashLen16(hash64(data) - seed0, seed1);
+ }
+
+ /**
+ * 计算64位City Hash值,种子1使用默认的{@link #k2}
+ *
+ * @param data 数据
+ * @param seed 种子2
+ * @return hash值
+ */
+ public static long hash64(byte[] data, long seed) {
+ return hash64(data, k2, seed);
+ }
+
+ /**
+ * 计算128位City Hash值
+ *
+ * @param data 数据
+ * @return hash值
+ */
+ public static Number128 hash128(byte[] data) {
+ int len = data.length;
+ return len >= 16 ?
+ hash128(data, 16,
+ new Number128(fetch64(data, 0), fetch64(data, 8) + k0)) :
+ hash128(data, 0, new Number128(k0, k1));
+ }
+
+ /**
+ * 计算128位City Hash值
+ *
+ * @param data 数据
+ * @param seed 种子
+ * @return hash值
+ */
+ public static Number128 hash128(byte[] data, Number128 seed) {
+ return hash128(data, 0, seed);
+ }
+
+ //------------------------------------------------------------------------------------------------------- Private method start
+ private static Number128 hash128(final byte[] byteArray, int start, final Number128 seed) {
+ int len = byteArray.length - start;
+
+ if (len < 128) {
+ return cityMurmur(Arrays.copyOfRange(byteArray, start, byteArray.length), seed);
+ }
+
+ // We expect len >= 128 to be the common case. Keep 56 bytes of state:
+ // v, w, x, y, and z.
+ Number128 v = new Number128(0L, 0L);
+ Number128 w = new Number128(0L, 0L);
+ long x = seed.getLowValue();
+ long y = seed.getHighValue();
+ long z = len * k1;
+ v.setLowValue(rotate(y ^ k1, 49) * k1 + fetch64(byteArray, start));
+ v.setHighValue(rotate(v.getLowValue(), 42) * k1 + fetch64(byteArray, start + 8));
+ w.setLowValue(rotate(y + z, 35) * k1 + x);
+ w.setHighValue(rotate(x + fetch64(byteArray, start + 88), 53) * k1);
+
+ // This is the same inner loop as CityHash64(), manually unrolled.
+ int pos = start;
+ do {
+ x = rotate(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
+ y = rotate(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
+ x ^= w.getHighValue();
+ y += v.getLowValue() + fetch64(byteArray, pos + 40);
+ z = rotate(z + w.getLowValue(), 33) * k1;
+ v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue());
+ w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16));
+
+ long swapValue = x;
+ x = z;
+ z = swapValue;
+ pos += 64;
+ x = rotate(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
+ y = rotate(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
+ x ^= w.getHighValue();
+ y += v.getLowValue() + fetch64(byteArray, pos + 40);
+ z = rotate(z + w.getLowValue(), 33) * k1;
+ v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue());
+ w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16));
+ swapValue = x;
+ x = z;
+ z = swapValue;
+ pos += 64;
+ len -= 128;
+ } while (len >= 128);
+ x += rotate(v.getLowValue() + z, 49) * k0;
+ y = y * k0 + rotate(w.getHighValue(), 37);
+ z = z * k0 + rotate(w.getLowValue(), 27);
+ w.setLowValue(w.getLowValue() * 9);
+ v.setLowValue(v.getLowValue() * k0);
+
+ // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
+ for (int tail_done = 0; tail_done < len; ) {
+ tail_done += 32;
+ y = rotate(x + y, 42) * k0 + v.getHighValue();
+ w.setLowValue(w.getLowValue() + fetch64(byteArray, pos + len - tail_done + 16));
+ x = x * k0 + w.getLowValue();
+ z += w.getHighValue() + fetch64(byteArray, pos + len - tail_done);
+ w.setHighValue(w.getHighValue() + v.getLowValue());
+ v = weakHashLen32WithSeeds(byteArray, pos + len - tail_done, v.getLowValue() + z, v.getHighValue());
+ v.setLowValue(v.getLowValue() * k0);
+ }
+ // At this point our 56 bytes of state should contain more than
+ // enough information for a strong 128-bit hash. We use two
+ // different 56-byte-to-8-byte hashes to get a 16-byte final result.
+ x = hashLen16(x, v.getLowValue());
+ y = hashLen16(y + z, w.getLowValue());
+ return new Number128(hashLen16(x + v.getHighValue(), w.getHighValue()) + y,
+ hashLen16(x + w.getHighValue(), y + v.getHighValue()));
+
+ }
+
+ private static int hash32Len0to4(final byte[] byteArray) {
+ int b = 0;
+ int c = 9;
+ int len = byteArray.length;
+ for (int v : byteArray) {
+ b = b * c1 + v;
+ c ^= b;
+ }
+ return fmix(mur(b, mur(len, c)));
+ }
+
+ private static int hash32Len5to12(final byte[] byteArray) {
+ int len = byteArray.length;
+ int a = len, b = len * 5, c = 9, d = b;
+ a += fetch32(byteArray, 0);
+ b += fetch32(byteArray, len - 4);
+ c += fetch32(byteArray, ((len >>> 1) & 4));
+ return fmix(mur(c, mur(b, mur(a, d))));
+ }
+
+ private static int hash32Len13to24(byte[] byteArray) {
+ int len = byteArray.length;
+ int a = fetch32(byteArray, (len >>> 1) - 4);
+ int b = fetch32(byteArray, 4);
+ int c = fetch32(byteArray, len - 8);
+ int d = fetch32(byteArray, (len >>> 1));
+ int e = fetch32(byteArray, 0);
+ int f = fetch32(byteArray, len - 4);
+ @SuppressWarnings("UnnecessaryLocalVariable")
+ int h = len;
+
+ return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, h)))))));
+ }
+
+ private static long hashLen0to16(byte[] byteArray) {
+ int len = byteArray.length;
+ if (len >= 8) {
+ long mul = k2 + len * 2;
+ long a = fetch64(byteArray, 0) + k2;
+ long b = fetch64(byteArray, len - 8);
+ long c = rotate(b, 37) * mul + a;
+ long d = (rotate(a, 25) + b) * mul;
+ return hashLen16(c, d, mul);
+ }
+ if (len >= 4) {
+ long mul = k2 + len * 2;
+ long a = fetch32(byteArray, 0) & 0xffffffffL;
+ return hashLen16(len + (a << 3), fetch32(byteArray, len - 4) & 0xffffffffL, mul);
+ }
+ if (len > 0) {
+ int a = byteArray[0] & 0xff;
+ int b = byteArray[len >>> 1] & 0xff;
+ int c = byteArray[len - 1] & 0xff;
+ int y = a + (b << 8);
+ int z = len + (c << 2);
+ return shiftMix(y * k2 ^ z * k0) * k2;
+ }
+ return k2;
+ }
+
+ // This probably works well for 16-byte strings as well, but it may be overkill in that case.
+ private static long hashLen17to32(byte[] byteArray) {
+ int len = byteArray.length;
+ long mul = k2 + len * 2;
+ long a = fetch64(byteArray, 0) * k1;
+ long b = fetch64(byteArray, 8);
+ long c = fetch64(byteArray, len - 8) * mul;
+ long d = fetch64(byteArray, len - 16) * k2;
+ return hashLen16(rotate(a + b, 43) + rotate(c, 30) + d,
+ a + rotate(b + k2, 18) + c, mul);
+ }
+
+ private static long hashLen33to64(byte[] byteArray) {
+ int len = byteArray.length;
+ long mul = k2 + len * 2;
+ long a = fetch64(byteArray, 0) * k2;
+ long b = fetch64(byteArray, 8);
+ long c = fetch64(byteArray, len - 24);
+ long d = fetch64(byteArray, len - 32);
+ long e = fetch64(byteArray, 16) * k2;
+ long f = fetch64(byteArray, 24) * 9;
+ long g = fetch64(byteArray, len - 8);
+ long h = fetch64(byteArray, len - 16) * mul;
+ long u = rotate(a + g, 43) + (rotate(b, 30) + c) * 9;
+ long v = ((a + g) ^ d) + f + 1;
+ long w = Long.reverseBytes((u + v) * mul) + h;
+ long x = rotate(e + f, 42) + c;
+ long y = (Long.reverseBytes((v + w) * mul) + g) * mul;
+ long z = e + f + c;
+ a = Long.reverseBytes((x + z) * mul + y) + b;
+ b = shiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+ }
+
+ private static long loadUnaligned64(final byte[] byteArray, final int start) {
+ long result = 0;
+ OrderIter orderIter = new OrderIter(8);
+ while (orderIter.hasNext()) {
+ int next = orderIter.next();
+ long value = (byteArray[next + start] & 0xffL) << (next * 8);
+ result |= value;
+ }
+ return result;
+ }
+
+ private static int loadUnaligned32(final byte[] byteArray, final int start) {
+ int result = 0;
+ OrderIter orderIter = new OrderIter(4);
+ while (orderIter.hasNext()) {
+ int next = orderIter.next();
+ int value = (byteArray[next + start] & 0xff) << (next * 8);
+ result |= value;
+ }
+ return result;
+ }
+
+ private static long fetch64(byte[] byteArray, final int start) {
+ return loadUnaligned64(byteArray, start);
+ }
+
+ private static int fetch32(byte[] byteArray, final int start) {
+ return loadUnaligned32(byteArray, start);
+ }
+
+ private static long rotate(long val, int shift) {
+ // Avoid shifting by 64: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >>> shift) | (val << (64 - shift)));
+ }
+
+ private static int rotate32(int val, int shift) {
+ // Avoid shifting by 32: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >>> shift) | (val << (32 - shift)));
+ }
+
+ private static long hashLen16(long u, long v, long mul) {
+ // Murmur-inspired hashing.
+ long a = (u ^ v) * mul;
+ a ^= (a >>> 47);
+ long b = (v ^ a) * mul;
+ b ^= (b >>> 47);
+ b *= mul;
+ return b;
+ }
+
+ private static long hashLen16(long u, long v) {
+ return hash128to64(new Number128(u, v));
+ }
+
+ private static long hash128to64(final Number128 number128) {
+ // Murmur-inspired hashing.
+ long a = (number128.getLowValue() ^ number128.getHighValue()) * kMul;
+ a ^= (a >>> 47);
+ long b = (number128.getHighValue() ^ a) * kMul;
+ b ^= (b >>> 47);
+ b *= kMul;
+ return b;
+ }
+
+ private static long shiftMix(long val) {
+ return val ^ (val >>> 47);
+ }
+
+ private static int fmix(int h) {
+ h ^= h >>> 16;
+ h *= 0x85ebca6b;
+ h ^= h >>> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >>> 16;
+ return h;
+ }
+
+ private static int mur(int a, int h) {
+ // Helper from Murmur3 for combining two 32-bit values.
+ a *= c1;
+ a = rotate32(a, 17);
+ a *= c2;
+ h ^= a;
+ h = rotate32(h, 19);
+ return h * 5 + 0xe6546b64;
+ }
+
+ private static Number128 weakHashLen32WithSeeds(
+ long w, long x, long y, long z, long a, long b) {
+ a += w;
+ b = rotate(b + a + z, 21);
+ long c = a;
+ a += x;
+ a += y;
+ b += rotate(a, 44);
+ return new Number128(a + z, b + c);
+ }
+
+ // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
+ private static Number128 weakHashLen32WithSeeds(
+ byte[] byteArray, int start, long a, long b) {
+ return weakHashLen32WithSeeds(fetch64(byteArray, start),
+ fetch64(byteArray, start + 8),
+ fetch64(byteArray, start + 16),
+ fetch64(byteArray, start + 24),
+ a,
+ b);
+ }
+
+ private static Number128 cityMurmur(final byte[] byteArray, Number128 seed) {
+ int len = byteArray.length;
+ long a = seed.getLowValue();
+ long b = seed.getHighValue();
+ long c;
+ long d;
+ int l = len - 16;
+ if (l <= 0) { // len <= 16
+ a = shiftMix(a * k1) * k1;
+ c = b * k1 + hashLen0to16(byteArray);
+ d = shiftMix(a + (len >= 8 ? fetch64(byteArray, 0) : c));
+ } else { // len > 16
+ c = hashLen16(fetch64(byteArray, len - 8) + k1, a);
+ d = hashLen16(b + len, c + fetch64(byteArray, len - 16));
+ a += d;
+ int pos = 0;
+ do {
+ a ^= shiftMix(fetch64(byteArray, pos) * k1) * k1;
+ a *= k1;
+ b ^= a;
+ c ^= shiftMix(fetch64(byteArray, pos + 8) * k1) * k1;
+ c *= k1;
+ d ^= c;
+ pos += 16;
+ l -= 16;
+ } while (l > 0);
+ }
+ a = hashLen16(a, c);
+ b = hashLen16(d, b);
+ return new Number128(a ^ b, hashLen16(b, a));
+ }
+
+ private static class OrderIter {
+ private static final boolean IS_LITTLE_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian"));
+
+ private final int size;
+ private int index;
+
+ OrderIter(int size) {
+ this.size = size;
+ }
+
+ boolean hasNext() {
+ return index < size;
+ }
+
+ int next() {
+ return IS_LITTLE_ENDIAN ? index++ : (size - 1 - index++);
+ }
+ }
+ //------------------------------------------------------------------------------------------------------- Private method end
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/MurmurHash.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/MurmurHash.java
similarity index 94%
rename from hutool-core/src/main/java/cn/hutool/core/lang/MurmurHash.java
rename to hutool-core/src/main/java/cn/hutool/core/lang/hash/MurmurHash.java
index 5caaa99bc..bba585400 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/MurmurHash.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/MurmurHash.java
@@ -1,11 +1,11 @@
-package cn.hutool.core.lang;
-
-import java.io.Serializable;
-import java.nio.charset.Charset;
+package cn.hutool.core.lang.hash;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+
/**
* Murmur3 32bit、64bit、128bit 哈希算法实现
* 此算法来自于:https://github.com/xlturing/Simhash4J/blob/master/src/main/java/bee/simhash/main/Murmur3.java
@@ -296,7 +296,7 @@ public class MurmurHash implements Serializable{
case 10:
k2 ^= (long) (data[tailStart + 9] & 0xff) << 8;
case 9:
- k2 ^= (long) (data[tailStart + 8] & 0xff);
+ k2 ^= data[tailStart + 8] & 0xff;
k2 *= C2;
k2 = Long.rotateLeft(k2, R3);
k2 *= C1;
@@ -317,7 +317,7 @@ public class MurmurHash implements Serializable{
case 2:
k1 ^= (long) (data[tailStart + 1] & 0xff) << 8;
case 1:
- k1 ^= (long) (data[tailStart] & 0xff);
+ k1 ^= data[tailStart] & 0xff;
k1 *= C1;
k1 = Long.rotateLeft(k1, R1);
k1 *= C2;
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java
new file mode 100644
index 000000000..10adf101b
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/Number128.java
@@ -0,0 +1,44 @@
+package cn.hutool.core.lang.hash;
+
+/**
+ * 128位数字表示,分高位和低位
+ *
+ * @author hexiufeng
+ * @since 5.2.5
+ */
+public class Number128 {
+
+ private long lowValue;
+ private long highValue;
+
+ /**
+ * 构造
+ *
+ * @param lowValue 低位
+ * @param highValue 高位
+ */
+ public Number128(long lowValue, long highValue) {
+ this.lowValue = lowValue;
+ this.highValue = highValue;
+ }
+
+ public long getLowValue() {
+ return lowValue;
+ }
+
+ public long getHighValue() {
+ return highValue;
+ }
+
+ public void setLowValue(long lowValue) {
+ this.lowValue = lowValue;
+ }
+
+ public void setHighValue(long hiValue) {
+ this.highValue = hiValue;
+ }
+
+ public long[] getLongArray() {
+ return new long[]{lowValue, highValue};
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/package-info.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/package-info.java
new file mode 100644
index 000000000..482cbdeb1
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 提供Hash算法的封装
+ *
+ * @author looly
+ *
+ */
+package cn.hutool.core.lang.hash;
\ No newline at end of file
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/Simhash.java b/hutool-core/src/main/java/cn/hutool/core/text/Simhash.java
index 1713f475c..74aa9fed4 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/Simhash.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/Simhash.java
@@ -10,7 +10,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-import cn.hutool.core.lang.MurmurHash;
+import cn.hutool.core.lang.hash.MurmurHash;
/**
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/HashUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/HashUtil.java
index cc9cc40ae..5e23a172a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/HashUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/HashUtil.java
@@ -1,19 +1,21 @@
package cn.hutool.core.util;
-import cn.hutool.core.lang.MurmurHash;
+import cn.hutool.core.lang.hash.CityHash;
+import cn.hutool.core.lang.hash.MurmurHash;
+import cn.hutool.core.lang.hash.Number128;
/**
* Hash算法大全
* 推荐使用FNV1算法
- *
- * @author Goodzzp,Looly
+ *
+ * @author Goodzzp, Looly
*/
public class HashUtil {
/**
* 加法hash
- *
- * @param key 字符串
+ *
+ * @param key 字符串
* @param prime 一个质数
* @return hash结果
*/
@@ -27,8 +29,8 @@ public class HashUtil {
/**
* 旋转hash
- *
- * @param key 输入字符串
+ *
+ * @param key 输入字符串
* @param prime 质数
* @return hash值
*/
@@ -46,7 +48,7 @@ public class HashUtil {
/**
* 一次一个hash
- *
+ *
* @param key 输入字符串
* @return 输出hash值
*/
@@ -66,7 +68,7 @@ public class HashUtil {
/**
* Bernstein's hash
- *
+ *
* @param key 输入字节数组
* @return 结果hash
*/
@@ -81,10 +83,10 @@ public class HashUtil {
/**
* Universal Hashing
- *
- * @param key 字节数组
+ *
+ * @param key 字节数组
* @param mask 掩码
- * @param tab tab
+ * @param tab tab
* @return hash值
*/
public static int universal(char[] key, int mask, int[] tab) {
@@ -121,10 +123,10 @@ public class HashUtil {
/**
* Zobrist Hashing
- *
- * @param key 字节数组
+ *
+ * @param key 字节数组
* @param mask 掩码
- * @param tab tab
+ * @param tab tab
* @return hash值
*/
public static int zobrist(char[] key, int mask, int[][] tab) {
@@ -137,7 +139,7 @@ public class HashUtil {
/**
* 改进的32位FNV算法1
- *
+ *
* @param data 数组
* @return hash结果
*/
@@ -157,7 +159,7 @@ public class HashUtil {
/**
* 改进的32位FNV算法1
- *
+ *
* @param data 字符串
* @return hash结果
*/
@@ -174,10 +176,10 @@ public class HashUtil {
hash += hash << 5;
return Math.abs(hash);
}
-
+
/**
* Thomas Wang的算法,整数hash
- *
+ *
* @param key 整数
* @return hash值
*/
@@ -193,7 +195,7 @@ public class HashUtil {
/**
* RS算法hash
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -212,7 +214,7 @@ public class HashUtil {
/**
* JS算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -228,7 +230,7 @@ public class HashUtil {
/**
* PJW算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -253,7 +255,7 @@ public class HashUtil {
/**
* ELF算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -274,7 +276,7 @@ public class HashUtil {
/**
* BKDR算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -291,7 +293,7 @@ public class HashUtil {
/**
* SDBM算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -307,7 +309,7 @@ public class HashUtil {
/**
* DJB算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -323,7 +325,7 @@ public class HashUtil {
/**
* DEK算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -339,7 +341,7 @@ public class HashUtil {
/**
* AP算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -356,7 +358,7 @@ public class HashUtil {
/**
* TianL Hash算法
- *
+ *
* @param str 字符串
* @return Hash值
*/
@@ -403,7 +405,7 @@ public class HashUtil {
/**
* JAVA自己带的算法
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -419,7 +421,7 @@ public class HashUtil {
/**
* 混合hash算法,输出64位的值
- *
+ *
* @param str 字符串
* @return hash值
*/
@@ -432,7 +434,7 @@ public class HashUtil {
/**
* 根据对象的内存地址生成相应的Hash值
- *
+ *
* @param obj 对象
* @return hash值
* @since 4.2.2
@@ -440,10 +442,10 @@ public class HashUtil {
public static int identityHashCode(Object obj) {
return System.identityHashCode(obj);
}
-
+
/**
* MurmurHash算法32-bit实现
- *
+ *
* @param data 数据
* @return hash值
* @since 4.3.3
@@ -451,10 +453,10 @@ public class HashUtil {
public static int murmur32(byte[] data) {
return MurmurHash.hash32(data);
}
-
+
/**
* MurmurHash算法64-bit实现
- *
+ *
* @param data 数据
* @return hash值
* @since 4.3.3
@@ -462,10 +464,10 @@ public class HashUtil {
public static long murmur64(byte[] data) {
return MurmurHash.hash64(data);
}
-
+
/**
* MurmurHash算法128-bit实现
- *
+ *
* @param data 数据
* @return hash值
* @since 4.3.3
@@ -473,4 +475,74 @@ public class HashUtil {
public static long[] murmur128(byte[] data) {
return MurmurHash.hash128(data);
}
+
+ /**
+ * CityHash算法32-bit实现
+ *
+ * @param data 数据
+ * @return hash值
+ * @since 5.2.5
+ */
+ public static int cityHash32(byte[] data) {
+ return CityHash.hash32(data);
+ }
+
+ /**
+ * CityHash算法64-bit实现,种子1使用默认的CityHash#k2
+ *
+ * @param data 数据
+ * @param seed 种子2
+ * @return hash值
+ * @since 5.2.5
+ */
+ public static long cityHash64(byte[] data, long seed) {
+ return CityHash.hash64(data, seed);
+ }
+
+ /**
+ * CityHash算法64-bit实现,种子1使用默认的CityHash#k2
+ *
+ * @param data 数据
+ * @param seed0 种子1
+ * @param seed1 种子2
+ * @return hash值
+ * @since 5.2.5
+ */
+ public static long cityHash64(byte[] data, long seed0, long seed1) {
+ return CityHash.hash64(data, seed0, seed1);
+ }
+
+ /**
+ * CityHash算法64-bit实现
+ *
+ * @param data 数据
+ * @return hash值
+ * @since 5.2.5
+ */
+ public static long cityHash64(byte[] data) {
+ return CityHash.hash64(data);
+ }
+
+ /**
+ * CityHash算法128-bit实现
+ *
+ * @param data 数据
+ * @return hash值
+ * @since 5.2.5
+ */
+ public static long[] cityHash128(byte[] data) {
+ return CityHash.hash128(data).getLongArray();
+ }
+
+ /**
+ * CityHash算法128-bit实现
+ *
+ * @param data 数据
+ * @param seed 种子
+ * @return hash值,long[0]:低位,long[1]:高位
+ * @since 5.2.5
+ */
+ public static long[] cityHash128(byte[] data, Number128 seed) {
+ return CityHash.hash128(data).getLongArray();
+ }
}
\ No newline at end of file