aboutsummaryrefslogtreecommitdiff
path: root/src/com/sun/org/apache/xerces/internal/util/SymbolHash.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/sun/org/apache/xerces/internal/util/SymbolHash.java')
-rw-r--r--src/com/sun/org/apache/xerces/internal/util/SymbolHash.java148
1 files changed, 126 insertions, 22 deletions
diff --git a/src/com/sun/org/apache/xerces/internal/util/SymbolHash.java b/src/com/sun/org/apache/xerces/internal/util/SymbolHash.java
index dee8383..2f16b36 100644
--- a/src/com/sun/org/apache/xerces/internal/util/SymbolHash.java
+++ b/src/com/sun/org/apache/xerces/internal/util/SymbolHash.java
@@ -1,13 +1,13 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright 2001, 2002,2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@@ -37,25 +37,40 @@ public class SymbolHash {
//
/** Default table size. */
- protected int fTableSize = 101;
+ protected static final int TABLE_SIZE = 101;
+
+ /** Maximum hash collisions per bucket. */
+ protected static final int MAX_HASH_COLLISIONS = 40;
+
+ protected static final int MULTIPLIERS_SIZE = 1 << 5;
+ protected static final int MULTIPLIERS_MASK = MULTIPLIERS_SIZE - 1;
//
// Data
//
+ /** Actual table size **/
+ protected int fTableSize;
+
/** Buckets. */
protected Entry[] fBuckets;
/** Number of elements. */
protected int fNum = 0;
+ /**
+ * Array of randomly selected hash function multipliers or <code>null</code>
+ * if the default String.hashCode() function should be used.
+ */
+ protected int[] fHashMultipliers;
+
//
// Constructors
//
/** Constructs a key table with the default size. */
public SymbolHash() {
- fBuckets = new Entry[fTableSize];
+ this(TABLE_SIZE);
}
/**
@@ -81,19 +96,37 @@ public class SymbolHash {
* @param value
*/
public void put(Object key, Object value) {
- int bucket = (key.hashCode() & 0x7FFFFFFF) % fTableSize;
- Entry entry = search(key, bucket);
- // replace old value
- if (entry != null) {
- entry.value = value;
+ // search for identical key
+ int collisionCount = 0;
+ final int hash = hash(key);
+ int bucket = hash % fTableSize;
+ for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {
+ if (key.equals(entry.key)) {
+ // replace old value
+ entry.value = value;
+ return;
+ }
+ ++collisionCount;
}
- // create new entry
- else {
- entry = new Entry(key, value, fBuckets[bucket]);
- fBuckets[bucket] = entry;
- fNum++;
+
+ if (fNum >= fTableSize) {
+ // Rehash the table if the number of entries
+ // would exceed the number of buckets.
+ rehash();
+ bucket = hash % fTableSize;
+ }
+ else if (collisionCount >= MAX_HASH_COLLISIONS && key instanceof String) {
+ // Select a new hash function and rehash the table if
+ // MAX_HASH_COLLISIONS is exceeded.
+ rebalance();
+ bucket = hash(key) % fTableSize;
}
+
+ // create new entry
+ Entry entry = new Entry(key, value, fBuckets[bucket]);
+ fBuckets[bucket] = entry;
+ ++fNum;
}
/**
@@ -103,7 +136,7 @@ public class SymbolHash {
* @return the value associated with the given key.
*/
public Object get(Object key) {
- int bucket = (key.hashCode() & 0x7FFFFFFF) % fTableSize;
+ int bucket = hash(key) % fTableSize;
Entry entry = search(key, bucket);
if (entry != null) {
return entry.value;
@@ -158,15 +191,17 @@ public class SymbolHash {
public SymbolHash makeClone() {
SymbolHash newTable = new SymbolHash(fTableSize);
newTable.fNum = fNum;
+ newTable.fHashMultipliers = fHashMultipliers != null ? (int[]) fHashMultipliers.clone() : null;
for (int i = 0; i < fTableSize; i++) {
- if (fBuckets[i] != null)
+ if (fBuckets[i] != null) {
newTable.fBuckets[i] = fBuckets[i].makeClone();
+ }
}
return newTable;
}
/**
- * Remove all key/value assocaition. This tries to save a bit of GC'ing
+ * Remove all key/value association. This tries to save a bit of GC'ing
* by at least keeping the fBuckets array around.
*/
public void clear() {
@@ -174,6 +209,7 @@ public class SymbolHash {
fBuckets[i] = null;
}
fNum = 0;
+ fHashMultipliers = null;
} // clear(): void
protected Entry search(Object key, int bucket) {
@@ -185,6 +221,74 @@ public class SymbolHash {
return null;
}
+ /**
+ * Returns a hashcode value for the specified key.
+ *
+ * @param key The key to hash.
+ */
+ protected int hash(Object key) {
+ if (fHashMultipliers == null || !(key instanceof String)) {
+ return key.hashCode() & 0x7FFFFFFF;
+ }
+ return hash0((String) key);
+ } // hash(Object):int
+
+ private int hash0(String symbol) {
+ int code = 0;
+ final int length = symbol.length();
+ final int[] multipliers = fHashMultipliers;
+ for (int i = 0; i < length; ++i) {
+ code = code * multipliers[i & MULTIPLIERS_MASK] + symbol.charAt(i);
+ }
+ return code & 0x7FFFFFFF;
+ } // hash0(String):int
+
+ /**
+ * Increases the capacity of and internally reorganizes this
+ * SymbolHash, in order to accommodate and access its entries more
+ * efficiently. This method is called automatically when the
+ * number of keys in the SymbolHash exceeds its number of buckets.
+ */
+ protected void rehash() {
+ rehashCommon((fBuckets.length << 1) + 1);
+ }
+
+ /**
+ * Randomly selects a new hash function and reorganizes this SymbolHash
+ * in order to more evenly distribute its entries across the table. This
+ * method is called automatically when the number keys in one of the
+ * SymbolHash's buckets exceeds MAX_HASH_COLLISIONS.
+ */
+ protected void rebalance() {
+ if (fHashMultipliers == null) {
+ fHashMultipliers = new int[MULTIPLIERS_SIZE];
+ }
+ PrimeNumberSequenceGenerator.generateSequence(fHashMultipliers);
+ rehashCommon(fBuckets.length);
+ }
+
+ private void rehashCommon(final int newCapacity) {
+
+ final int oldCapacity = fBuckets.length;
+ final Entry[] oldTable = fBuckets;
+
+ final Entry[] newTable = new Entry[newCapacity];
+
+ fBuckets = newTable;
+ fTableSize = fBuckets.length;
+
+ for (int i = oldCapacity; i-- > 0;) {
+ for (Entry old = oldTable[i]; old != null; ) {
+ Entry e = old;
+ old = old.next;
+
+ int index = hash(e.key) % newCapacity;
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+ }
+
//
// Classes
//