Thu, 31 Aug 2017 15:18:52 +0800
merge
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package com.sun.xml.internal.dtdparser; |
aoqi@0 | 27 | |
aoqi@0 | 28 | import java.util.Enumeration; |
aoqi@0 | 29 | |
aoqi@0 | 30 | |
aoqi@0 | 31 | // This could be replaced by Collections class unless we want |
aoqi@0 | 32 | // to be able to run on JDK 1.1 |
aoqi@0 | 33 | |
aoqi@0 | 34 | |
aoqi@0 | 35 | /** |
aoqi@0 | 36 | * This class implements a special purpose hashtable. It works like a |
aoqi@0 | 37 | * normal <code>java.util.Hashtable</code> except that: <OL> |
aoqi@0 | 38 | * <p/> |
aoqi@0 | 39 | * <LI> Keys to "get" are strings which are known to be interned, |
aoqi@0 | 40 | * so that "==" is used instead of "String.equals". (Interning |
aoqi@0 | 41 | * could be document-relative instead of global.) |
aoqi@0 | 42 | * <p/> |
aoqi@0 | 43 | * <LI> It's not synchronized, since it's to be used only by |
aoqi@0 | 44 | * one thread at a time. |
aoqi@0 | 45 | * <p/> |
aoqi@0 | 46 | * <LI> The keys () enumerator allocates no memory, with live |
aoqi@0 | 47 | * updates to the data disallowed. |
aoqi@0 | 48 | * <p/> |
aoqi@0 | 49 | * <LI> It's got fewer bells and whistles: fixed threshold and |
aoqi@0 | 50 | * load factor, no JDK 1.2 collection support, only keys can be |
aoqi@0 | 51 | * enumerated, things can't be removed, simpler inheritance; more. |
aoqi@0 | 52 | * <p/> |
aoqi@0 | 53 | * </OL> |
aoqi@0 | 54 | * <p/> |
aoqi@0 | 55 | * <P> The overall result is that it's less expensive to use these in |
aoqi@0 | 56 | * performance-critical locations, in terms both of CPU and memory, |
aoqi@0 | 57 | * than <code>java.util.Hashtable</code> instances. In this package |
aoqi@0 | 58 | * it makes a significant difference when normalizing attributes, |
aoqi@0 | 59 | * which is done for each start-element construct. |
aoqi@0 | 60 | * |
aoqi@0 | 61 | * @version $Revision: 1.2 $ |
aoqi@0 | 62 | */ |
aoqi@0 | 63 | final class SimpleHashtable implements Enumeration { |
aoqi@0 | 64 | // entries ... |
aoqi@0 | 65 | private Entry table[]; |
aoqi@0 | 66 | |
aoqi@0 | 67 | // currently enumerated key |
aoqi@0 | 68 | private Entry current = null; |
aoqi@0 | 69 | private int currentBucket = 0; |
aoqi@0 | 70 | |
aoqi@0 | 71 | private int count; |
aoqi@0 | 72 | private int threshold; |
aoqi@0 | 73 | |
aoqi@0 | 74 | private static final float loadFactor = 0.75f; |
aoqi@0 | 75 | |
aoqi@0 | 76 | |
aoqi@0 | 77 | /** |
aoqi@0 | 78 | * Constructs a new, empty hashtable with the specified initial |
aoqi@0 | 79 | * capacity. |
aoqi@0 | 80 | * |
aoqi@0 | 81 | * @param initialCapacity the initial capacity of the hashtable. |
aoqi@0 | 82 | */ |
aoqi@0 | 83 | public SimpleHashtable(int initialCapacity) { |
aoqi@0 | 84 | if (initialCapacity < 0) |
aoqi@0 | 85 | throw new IllegalArgumentException("Illegal Capacity: " + |
aoqi@0 | 86 | initialCapacity); |
aoqi@0 | 87 | if (initialCapacity == 0) |
aoqi@0 | 88 | initialCapacity = 1; |
aoqi@0 | 89 | table = new Entry[initialCapacity]; |
aoqi@0 | 90 | threshold = (int) (initialCapacity * loadFactor); |
aoqi@0 | 91 | } |
aoqi@0 | 92 | |
aoqi@0 | 93 | /** |
aoqi@0 | 94 | * Constructs a new, empty hashtable with a default capacity. |
aoqi@0 | 95 | */ |
aoqi@0 | 96 | public SimpleHashtable() { |
aoqi@0 | 97 | this(11); |
aoqi@0 | 98 | } |
aoqi@0 | 99 | |
aoqi@0 | 100 | /** |
aoqi@0 | 101 | */ |
aoqi@0 | 102 | public void clear() { |
aoqi@0 | 103 | count = 0; |
aoqi@0 | 104 | currentBucket = 0; |
aoqi@0 | 105 | current = null; |
aoqi@0 | 106 | for (int i = 0; i < table.length; i++) |
aoqi@0 | 107 | table[i] = null; |
aoqi@0 | 108 | } |
aoqi@0 | 109 | |
aoqi@0 | 110 | /** |
aoqi@0 | 111 | * Returns the number of keys in this hashtable. |
aoqi@0 | 112 | * |
aoqi@0 | 113 | * @return the number of keys in this hashtable. |
aoqi@0 | 114 | */ |
aoqi@0 | 115 | public int size() { |
aoqi@0 | 116 | return count; |
aoqi@0 | 117 | } |
aoqi@0 | 118 | |
aoqi@0 | 119 | /** |
aoqi@0 | 120 | * Returns an enumeration of the keys in this hashtable. |
aoqi@0 | 121 | * |
aoqi@0 | 122 | * @return an enumeration of the keys in this hashtable. |
aoqi@0 | 123 | * @see Enumeration |
aoqi@0 | 124 | */ |
aoqi@0 | 125 | public Enumeration keys() { |
aoqi@0 | 126 | currentBucket = 0; |
aoqi@0 | 127 | current = null; |
aoqi@0 | 128 | return this; |
aoqi@0 | 129 | } |
aoqi@0 | 130 | |
aoqi@0 | 131 | /** |
aoqi@0 | 132 | * Used to view this as an enumeration; returns true if there |
aoqi@0 | 133 | * are more keys to be enumerated. |
aoqi@0 | 134 | */ |
aoqi@0 | 135 | public boolean hasMoreElements() { |
aoqi@0 | 136 | if (current != null) |
aoqi@0 | 137 | return true; |
aoqi@0 | 138 | while (currentBucket < table.length) { |
aoqi@0 | 139 | current = table[currentBucket++]; |
aoqi@0 | 140 | if (current != null) |
aoqi@0 | 141 | return true; |
aoqi@0 | 142 | } |
aoqi@0 | 143 | return false; |
aoqi@0 | 144 | } |
aoqi@0 | 145 | |
aoqi@0 | 146 | /** |
aoqi@0 | 147 | * Used to view this as an enumeration; returns the next key |
aoqi@0 | 148 | * in the enumeration. |
aoqi@0 | 149 | */ |
aoqi@0 | 150 | public Object nextElement() { |
aoqi@0 | 151 | Object retval; |
aoqi@0 | 152 | |
aoqi@0 | 153 | if (current == null) |
aoqi@0 | 154 | throw new IllegalStateException(); |
aoqi@0 | 155 | retval = current.key; |
aoqi@0 | 156 | current = current.next; |
aoqi@0 | 157 | return retval; |
aoqi@0 | 158 | } |
aoqi@0 | 159 | |
aoqi@0 | 160 | |
aoqi@0 | 161 | /** |
aoqi@0 | 162 | * Returns the value to which the specified key is mapped in this hashtable. |
aoqi@0 | 163 | */ |
aoqi@0 | 164 | public Object get(String key) { |
aoqi@0 | 165 | Entry tab[] = table; |
aoqi@0 | 166 | int hash = key.hashCode(); |
aoqi@0 | 167 | int index = (hash & 0x7FFFFFFF) % tab.length; |
aoqi@0 | 168 | for (Entry e = tab[index]; e != null; e = e.next) { |
aoqi@0 | 169 | if ((e.hash == hash) && (e.key == key)) |
aoqi@0 | 170 | return e.value; |
aoqi@0 | 171 | } |
aoqi@0 | 172 | return null; |
aoqi@0 | 173 | } |
aoqi@0 | 174 | |
aoqi@0 | 175 | /** |
aoqi@0 | 176 | * Returns the value to which the specified key is mapped in this |
aoqi@0 | 177 | * hashtable ... the key isn't necessarily interned, though. |
aoqi@0 | 178 | */ |
aoqi@0 | 179 | public Object getNonInterned(String key) { |
aoqi@0 | 180 | Entry tab[] = table; |
aoqi@0 | 181 | int hash = key.hashCode(); |
aoqi@0 | 182 | int index = (hash & 0x7FFFFFFF) % tab.length; |
aoqi@0 | 183 | for (Entry e = tab[index]; e != null; e = e.next) { |
aoqi@0 | 184 | if ((e.hash == hash) && e.key.equals(key)) |
aoqi@0 | 185 | return e.value; |
aoqi@0 | 186 | } |
aoqi@0 | 187 | return null; |
aoqi@0 | 188 | } |
aoqi@0 | 189 | |
aoqi@0 | 190 | /** |
aoqi@0 | 191 | * Increases the capacity of and internally reorganizes this |
aoqi@0 | 192 | * hashtable, in order to accommodate and access its entries more |
aoqi@0 | 193 | * efficiently. This method is called automatically when the |
aoqi@0 | 194 | * number of keys in the hashtable exceeds this hashtable's capacity |
aoqi@0 | 195 | * and load factor. |
aoqi@0 | 196 | */ |
aoqi@0 | 197 | private void rehash() { |
aoqi@0 | 198 | int oldCapacity = table.length; |
aoqi@0 | 199 | Entry oldMap[] = table; |
aoqi@0 | 200 | |
aoqi@0 | 201 | int newCapacity = oldCapacity * 2 + 1; |
aoqi@0 | 202 | Entry newMap[] = new Entry[newCapacity]; |
aoqi@0 | 203 | |
aoqi@0 | 204 | threshold = (int) (newCapacity * loadFactor); |
aoqi@0 | 205 | table = newMap; |
aoqi@0 | 206 | |
aoqi@0 | 207 | /* |
aoqi@0 | 208 | System.out.println("rehash old=" + oldCapacity |
aoqi@0 | 209 | + ", new=" + newCapacity |
aoqi@0 | 210 | + ", thresh=" + threshold |
aoqi@0 | 211 | + ", count=" + count); |
aoqi@0 | 212 | */ |
aoqi@0 | 213 | |
aoqi@0 | 214 | for (int i = oldCapacity; i-- > 0;) { |
aoqi@0 | 215 | for (Entry old = oldMap[i]; old != null;) { |
aoqi@0 | 216 | Entry e = old; |
aoqi@0 | 217 | old = old.next; |
aoqi@0 | 218 | |
aoqi@0 | 219 | int index = (e.hash & 0x7FFFFFFF) % newCapacity; |
aoqi@0 | 220 | e.next = newMap[index]; |
aoqi@0 | 221 | newMap[index] = e; |
aoqi@0 | 222 | } |
aoqi@0 | 223 | } |
aoqi@0 | 224 | } |
aoqi@0 | 225 | |
aoqi@0 | 226 | /** |
aoqi@0 | 227 | * Maps the specified <code>key</code> to the specified |
aoqi@0 | 228 | * <code>value</code> in this hashtable. Neither the key nor the |
aoqi@0 | 229 | * value can be <code>null</code>. |
aoqi@0 | 230 | * <p/> |
aoqi@0 | 231 | * <P>The value can be retrieved by calling the <code>get</code> method |
aoqi@0 | 232 | * with a key that is equal to the original key. |
aoqi@0 | 233 | */ |
aoqi@0 | 234 | public Object put(Object key, Object value) { |
aoqi@0 | 235 | // Make sure the value is not null |
aoqi@0 | 236 | if (value == null) { |
aoqi@0 | 237 | throw new NullPointerException(); |
aoqi@0 | 238 | } |
aoqi@0 | 239 | |
aoqi@0 | 240 | // Makes sure the key is not already in the hashtable. |
aoqi@0 | 241 | Entry tab[] = table; |
aoqi@0 | 242 | int hash = key.hashCode(); |
aoqi@0 | 243 | int index = (hash & 0x7FFFFFFF) % tab.length; |
aoqi@0 | 244 | for (Entry e = tab[index]; e != null; e = e.next) { |
aoqi@0 | 245 | // if ((e.hash == hash) && e.key.equals(key)) { |
aoqi@0 | 246 | if ((e.hash == hash) && (e.key == key)) { |
aoqi@0 | 247 | Object old = e.value; |
aoqi@0 | 248 | e.value = value; |
aoqi@0 | 249 | return old; |
aoqi@0 | 250 | } |
aoqi@0 | 251 | } |
aoqi@0 | 252 | |
aoqi@0 | 253 | if (count >= threshold) { |
aoqi@0 | 254 | // Rehash the table if the threshold is exceeded |
aoqi@0 | 255 | rehash(); |
aoqi@0 | 256 | |
aoqi@0 | 257 | tab = table; |
aoqi@0 | 258 | index = (hash & 0x7FFFFFFF) % tab.length; |
aoqi@0 | 259 | } |
aoqi@0 | 260 | |
aoqi@0 | 261 | // Creates the new entry. |
aoqi@0 | 262 | Entry e = new Entry(hash, key, value, tab[index]); |
aoqi@0 | 263 | tab[index] = e; |
aoqi@0 | 264 | count++; |
aoqi@0 | 265 | return null; |
aoqi@0 | 266 | } |
aoqi@0 | 267 | |
aoqi@0 | 268 | |
aoqi@0 | 269 | /** |
aoqi@0 | 270 | * Hashtable collision list. |
aoqi@0 | 271 | */ |
aoqi@0 | 272 | private static class Entry { |
aoqi@0 | 273 | int hash; |
aoqi@0 | 274 | Object key; |
aoqi@0 | 275 | Object value; |
aoqi@0 | 276 | Entry next; |
aoqi@0 | 277 | |
aoqi@0 | 278 | protected Entry(int hash, Object key, Object value, Entry next) { |
aoqi@0 | 279 | this.hash = hash; |
aoqi@0 | 280 | this.key = key; |
aoqi@0 | 281 | this.value = value; |
aoqi@0 | 282 | this.next = next; |
aoqi@0 | 283 | } |
aoqi@0 | 284 | } |
aoqi@0 | 285 | } |