String 在字符串的开头与字符串的结尾是否具有高可变性无关紧要。
为了测试这一点,下面的代码模拟了 Java 8 的 HashMap 类的哈希表逻辑。 tableSizeFor 和 hash 方法是从 JDK 源代码中复制而来的。
代码将创建 60 个字符串,它们的区别仅在于前 7 个字符或后 7 个字符。然后它将构建一个具有适当容量的哈希表并计算哈希桶冲突的次数。
从输出中可以看出,无论被散列的字符串的前导或尾随可变性如何,冲突计数都是相同的(在统计范围内)。
输出
Count: 1000 Collisions: 384 By collision size: {1=240, 2=72}
Count: 1000 Collisions: 278 By collision size: {1=191, 2=30, 3=3, 4=3, 6=1}
Count: 100000 Collisions: 13876 By collision size: {1=12706, 2=579, 3=4}
Count: 100000 Collisions: 15742 By collision size: {1=12644, 2=1378, 3=110, 4=3}
Count: 10000000 Collisions: 2705759 By collision size: {1=1703714, 2=381705, 3=65050, 4=9417, 5=1038, 6=101, 7=3}
Count: 10000000 Collisions: 2626728 By collision size: {1=1698957, 2=365663, 3=56156, 4=6278, 5=535, 6=27, 7=4}
测试代码
public class Test {
public static void main(String[] args) throws Exception {
//
test(1000, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_%07d");
test(1000, "%07d_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
test(100000, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_%07d");
test(100000, "%07d_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
test(10000000, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_%07d");
test(10000000, "%07d_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
}
private static void test(int count, String format) {
// Allocate hash-table
final int initialCapacity = count * 4 / 3 + 1;
final int tableSize = tableSizeFor(initialCapacity);
int[] tab = new int[tableSize];
// Build strings, calculate hash bucket, and increment bucket counter
for (int i = 0; i < count; i++) {
String key = String.format(format, i);
int hash = hash(key);
int bucket = (tableSize - 1) & hash;
tab[bucket]++;
}
// Collect collision counts, i.e. counts > 1
// E.g. a bucket count of 3 means 1 original value plus 2 collisions
int total = 0;
Map<Integer, AtomicInteger> collisions = new TreeMap<>();
for (int i = 0; i < tableSize; i++)
if (tab[i] > 1) {
total += tab[i] - 1;
collisions.computeIfAbsent(tab[i] - 1, c -> new AtomicInteger()).incrementAndGet();
}
// Print result
System.out.printf("Count: %-8d Collisions: %-7d By collision size: %s%n", count, total, collisions);
}
static final int MAXIMUM_CAPACITY = 1 << 30;
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
}