19
19
20
20
import java .util .ArrayList ;
21
21
import java .util .Hashtable ;
22
+ import java .util .Random ;
22
23
23
24
import org .apache .xerces .impl .dtd .models .CMAny ;
24
25
import org .apache .xerces .impl .dtd .models .CMBinOp ;
@@ -2639,6 +2640,26 @@ public ChildrenList () {}
2639
2640
* @author Andy Clark, IBM
2640
2641
*/
2641
2642
protected static final class QNameHashtable {
2643
+
2644
+ private static final class PrimeNumberSequenceGenerator {
2645
+
2646
+ private static int [] PRIMES = {
2647
+ 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 , 59 ,
2648
+ 61 , 67 , 71 , 73 , 79 , 83 , 89 , 97 , 101 , 103 , 107 , 109 , 113 , 127 , 131 , 137 ,
2649
+ 139 , 149 , 151 , 157 , 163 , 167 , 173 , 179 , 181 , 191 , 193 , 197 , 199 , 211 , 223 , 227 ,
2650
+ 229 , 233 , 239 , 241 , 251 , 257 , 263 , 269 , 271 , 277 , 281 , 283 , 293 , 307 , 311 , 313 ,
2651
+ 317 , 331 , 337 , 347 , 349 , 353 , 359 , 367 , 373 , 379 , 383 , 389 , 397 , 401 , 409 , 419 ,
2652
+ 421 , 431 , 433 , 439 , 443 , 449 , 457 , 461 , 463 , 467 , 479 , 487 , 491 , 499 , 503 , 509 ,
2653
+ 521 , 523 , 541 , 547 , 557 , 563 , 569 , 571 , 577 , 587 , 593 , 599 , 601 , 607 , 613 , 617 ,
2654
+ 619 , 631 , 641 , 643 , 647 , 653 , 659 , 661 , 673 , 677 , 683 , 691 , 701 , 709 , 719 , 727 };
2655
+
2656
+ static void generateSequence (int [] arrayToFill ) {
2657
+ Random r = new Random ();
2658
+ for (int i = 0 ; i < arrayToFill .length ; ++i ) {
2659
+ arrayToFill [i ] = PRIMES [r .nextInt (PRIMES .length )];
2660
+ }
2661
+ }
2662
+ }
2642
2663
2643
2664
//
2644
2665
// Constants
@@ -2651,19 +2672,37 @@ protected static final class QNameHashtable {
2651
2672
// that we get a better distribution for hashing. -Ac
2652
2673
/** Hashtable size (101). */
2653
2674
private static final int HASHTABLE_SIZE = 101 ;
2675
+
2676
+ /** Maximum hash collisions per bucket for a table with load factor == 1. */
2677
+ private static final int MAX_HASH_COLLISIONS = 40 ;
2678
+
2679
+ private static final int MULTIPLIERS_SIZE = 1 << 5 ;
2680
+ private static final int MULTIPLIERS_MASK = MULTIPLIERS_SIZE - 1 ;
2654
2681
2655
2682
//
2656
2683
// Data
2657
2684
//
2658
2685
private Object [][] fHashTable = new Object [HASHTABLE_SIZE ][];
2686
+
2687
+ /** actual table size **/
2688
+ private int fTableSize = HASHTABLE_SIZE ;
2689
+
2690
+ /** The total number of entries in the hash table. */
2691
+ private int fCount = 0 ;
2692
+
2693
+ /**
2694
+ * Array of randomly selected hash function multipliers or <code>null</code>
2695
+ * if the default String.hashCode() function should be used.
2696
+ */
2697
+ private int [] fHashMultipliers ;
2659
2698
2660
2699
//
2661
2700
// Public methods
2662
2701
//
2663
2702
/** Associates the given value with the specified key tuple. */
2664
2703
public void put (String key , int value ) {
2665
2704
2666
- int hash = (key . hashCode ( ) & 0x7FFFFFFF ) % HASHTABLE_SIZE ;
2705
+ int hash = (hash ( key ) & 0x7FFFFFFF ) % fTableSize ;
2667
2706
Object [] bucket = fHashTable [hash ];
2668
2707
2669
2708
if (bucket == null ) {
@@ -2672,6 +2711,11 @@ public void put(String key, int value) {
2672
2711
bucket [1 ] = key ;
2673
2712
bucket [2 ] = new int []{value };
2674
2713
fHashTable [hash ] = bucket ;
2714
+ if (++fCount > fTableSize ) {
2715
+ // Rehash the table if the number of entries
2716
+ // would exceed the number of buckets.
2717
+ rehash ();
2718
+ }
2675
2719
} else {
2676
2720
int count = ((int [])bucket [0 ])[0 ];
2677
2721
int offset = 1 + 2 *count ;
@@ -2692,10 +2736,20 @@ public void put(String key, int value) {
2692
2736
}
2693
2737
j += 2 ;
2694
2738
}
2695
- if (! found ) {
2739
+ if (!found ) {
2696
2740
bucket [offset ++] = key ;
2697
2741
bucket [offset ]= new int []{value };
2698
2742
((int [])bucket [0 ])[0 ] = ++count ;
2743
+ if (++fCount > fTableSize ) {
2744
+ // Rehash the table if the number of entries
2745
+ // would exceed the number of buckets.
2746
+ rehash ();
2747
+ }
2748
+ else if (count > MAX_HASH_COLLISIONS ) {
2749
+ // Select a new hash function and rehash the table if
2750
+ // MAX_HASH_COLLISIONS is exceeded.
2751
+ rebalance ();
2752
+ }
2699
2753
}
2700
2754
2701
2755
}
@@ -2706,7 +2760,7 @@ public void put(String key, int value) {
2706
2760
2707
2761
/** Returns the value associated with the specified key tuple. */
2708
2762
public int get (String key ) {
2709
- int hash = (key . hashCode ( ) & 0x7FFFFFFF ) % HASHTABLE_SIZE ;
2763
+ int hash = (hash ( key ) & 0x7FFFFFFF ) % fTableSize ;
2710
2764
Object [] bucket = fHashTable [hash ];
2711
2765
2712
2766
if (bucket == null ) {
@@ -2724,6 +2778,93 @@ public int get(String key) {
2724
2778
return -1 ;
2725
2779
2726
2780
} // get(int,String,String)
2781
+
2782
+ public int hash (String symbol ) {
2783
+ if (fHashMultipliers == null ) {
2784
+ return symbol .hashCode ();
2785
+ }
2786
+ return hash0 (symbol );
2787
+ } // hash(String):int
2788
+
2789
+ private int hash0 (String symbol ) {
2790
+ int code = 0 ;
2791
+ final int length = symbol .length ();
2792
+ final int [] multipliers = fHashMultipliers ;
2793
+ for (int i = 0 ; i < length ; ++i ) {
2794
+ code = code * multipliers [i & MULTIPLIERS_MASK ] + symbol .charAt (i );
2795
+ }
2796
+ return code ;
2797
+ } // hash0(String):int
2798
+
2799
+ private void rehash () {
2800
+ rehashCommon (fHashTable .length * 2 + 1 );
2801
+ } // rehash()
2802
+
2803
+ private void rebalance () {
2804
+ if (fHashMultipliers == null ) {
2805
+ fHashMultipliers = new int [MULTIPLIERS_SIZE ];
2806
+ }
2807
+ PrimeNumberSequenceGenerator .generateSequence (fHashMultipliers );
2808
+ rehashCommon (fHashTable .length );
2809
+ } // rebalance()
2810
+
2811
+ private void rehashCommon (final int newCapacity ) {
2812
+
2813
+ final int oldCapacity = fHashTable .length ;
2814
+ final Object [][] oldTable = fHashTable ;
2815
+
2816
+ final Object [][] newTable = new Object [newCapacity ][];
2817
+
2818
+ fHashTable = newTable ;
2819
+ fTableSize = fHashTable .length ;
2820
+
2821
+ for (int i = 0 ; i < oldCapacity ; ++i ) {
2822
+ final Object [] oldBucket = oldTable [i ];
2823
+ if (oldBucket != null ) {
2824
+ final int oldCount = ((int []) oldBucket [0 ])[0 ];
2825
+ boolean oldBucketReused = false ;
2826
+ int k = 1 ;
2827
+ for (int j = 0 ; j < oldCount ; ++j ) {
2828
+ final String key = (String ) oldBucket [k ];
2829
+ final Object value = oldBucket [k +1 ];
2830
+
2831
+ final int hash = (hash (key ) & 0x7FFFFFFF ) % fTableSize ;
2832
+ Object [] bucket = fHashTable [hash ];
2833
+
2834
+ if (bucket == null ) {
2835
+ if (oldBucketReused ) {
2836
+ bucket = new Object [1 + 2 *INITIAL_BUCKET_SIZE ];
2837
+ bucket [0 ] = new int []{1 };
2838
+ }
2839
+ else {
2840
+ bucket = oldBucket ;
2841
+ ((int [])bucket [0 ])[0 ] = 1 ;
2842
+ oldBucketReused = true ;
2843
+ }
2844
+ bucket [1 ] = key ;
2845
+ bucket [2 ] = value ;
2846
+ fHashTable [hash ] = bucket ;
2847
+ }
2848
+ else {
2849
+ int count = ((int [])bucket [0 ])[0 ];
2850
+ int offset = 1 + 2 *count ;
2851
+ if (offset == bucket .length ) {
2852
+ int newSize = count + INITIAL_BUCKET_SIZE ;
2853
+ Object [] newBucket = new Object [1 + 2 *newSize ];
2854
+ System .arraycopy (bucket , 0 , newBucket , 0 , offset );
2855
+ bucket = newBucket ;
2856
+ fHashTable [hash ] = bucket ;
2857
+ }
2858
+ bucket [offset ++] = key ;
2859
+ bucket [offset ]= value ;
2860
+ ((int [])bucket [0 ])[0 ] = ++count ;
2861
+ }
2862
+ k += 2 ;
2863
+ }
2864
+ }
2865
+ }
2866
+
2867
+ } // rehashCommon(int)
2727
2868
2728
2869
} // class QNameHashtable
2729
2870
0 commit comments