Showing the changes to java/pkg/java/util/EnumMap.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,799 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.util; | ||
| 27 | |||
| 28 | import java.util.Map.Entry; | ||
| 29 | import sun.misc.SharedSecrets; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * A specialized {@link Map} implementation for use with enum type keys. All | ||
| 33 | * of the keys in an enum map must come from a single enum type that is | ||
| 34 | * specified, explicitly or implicitly, when the map is created. Enum maps | ||
| 35 | * are represented internally as arrays. This representation is extremely | ||
| 36 | * compact and efficient. | ||
| 37 | * | ||
| 38 | * <p>Enum maps are maintained in the <i>natural order</i> of their keys | ||
| 39 | * (the order in which the enum constants are declared). This is reflected | ||
| 40 | * in the iterators returned by the collections views ({@link #keySet()}, | ||
| 41 | * {@link #entrySet()}, and {@link #values()}). | ||
| 42 | * | ||
| 43 | * <p>Iterators returned by the collection views are <i>weakly consistent</i>: | ||
| 44 | * they will never throw {@link ConcurrentModificationException} and they may | ||
| 45 | * or may not show the effects of any modifications to the map that occur while | ||
| 46 | * the iteration is in progress. | ||
| 47 | * | ||
| 48 | * <p>Null keys are not permitted. Attempts to insert a null key will | ||
| 49 | * throw {@link NullPointerException}. Attempts to test for the | ||
| 50 | * presence of a null key or to remove one will, however, function properly. | ||
| 51 | * Null values are permitted. | ||
| 52 | |||
| 53 | * <P>Like most collection implementations <tt>EnumMap</tt> is not | ||
| 54 | * synchronized. If multiple threads access an enum map concurrently, and at | ||
| 55 | * least one of the threads modifies the map, it should be synchronized | ||
| 56 | * externally. This is typically accomplished by synchronizing on some | ||
| 57 | * object that naturally encapsulates the enum map. If no such object exists, | ||
| 58 | * the map should be "wrapped" using the {@link Collections#synchronizedMap} | ||
| 59 | * method. This is best done at creation time, to prevent accidental | ||
| 60 | * unsynchronized access: | ||
| 61 | * | ||
| 62 | * <pre> | ||
| 63 | * Map<EnumKey, V> m | ||
| 64 | * = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...)); | ||
| 65 | * </pre> | ||
| 66 | * | ||
| 67 | * <p>Implementation note: All basic operations execute in constant time. | ||
| 68 | * They are likely (though not guaranteed) to be faster than their | ||
| 69 | * {@link HashMap} counterparts. | ||
| 70 | * | ||
| 71 | * <p>This class is a member of the | ||
| 72 | * <a href="{@docRoot}/../technotes/guides/collections/index.html"> | ||
| 73 | * Java Collections Framework</a>. | ||
| 74 | * | ||
| 75 | * @author Josh Bloch | ||
| 76 | * @see EnumSet | ||
| 77 | * @since 1.5 | ||
| 78 | */ | ||
| 79 | public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> | ||
| 80 | implements java.io.Serializable, Cloneable | ||
| 81 | { | ||
| 82 | /** | ||
| 83 | * The <tt>Class</tt> object for the enum type of all the keys of this map. | ||
| 84 | * | ||
| 85 | * @serial | ||
| 86 | */ | ||
| 87 | private final Class<K> keyType; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * All of the values comprising K. (Cached for performance.) | ||
| 91 | */ | ||
| 92 | private transient K[] keyUniverse; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Array representation of this map. The ith element is the value | ||
| 96 | * to which universe[i] is currently mapped, or null if it isn't | ||
| 97 | * mapped to anything, or NULL if it's mapped to null. | ||
| 98 | */ | ||
| 99 | private transient Object[] vals; | ||
| 100 | |||
| 101 | /** | ||
| 102 | * The number of mappings in this map. | ||
| 103 | */ | ||
| 104 | private transient int size = 0; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Distinguished non-null value for representing null values. | ||
| 108 | */ | ||
| 109 | private static final Object NULL = new Integer(0); | ||
| 110 | |||
| 111 | private Object maskNull(Object value) { | ||
| 112 | return (value == null ? NULL : value); | ||
| 113 | } | ||
| 114 | |||
| 115 | private V unmaskNull(Object value) { | ||
| 116 | return (V) (value == NULL ? null : value); | ||
| 117 | } | ||
| 118 | |||
| 119 | private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0]; | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Creates an empty enum map with the specified key type. | ||
| 123 | * | ||
| 124 | * @param keyType the class object of the key type for this enum map | ||
| 125 | * @throws NullPointerException if <tt>keyType</tt> is null | ||
| 126 | */ | ||
| 127 | public EnumMap(Class<K> keyType) { | ||
| 128 | this.keyType = keyType; | ||
| 129 | keyUniverse = getKeyUniverse(keyType); | ||
| 130 | vals = new Object[keyUniverse.length]; | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Creates an enum map with the same key type as the specified enum | ||
| 135 | * map, initially containing the same mappings (if any). | ||
| 136 | * | ||
| 137 | * @param m the enum map from which to initialize this enum map | ||
| 138 | * @throws NullPointerException if <tt>m</tt> is null | ||
| 139 | */ | ||
| 140 | public EnumMap(EnumMap<K, ? extends V> m) { | ||
| 141 | keyType = m.keyType; | ||
| 142 | keyUniverse = m.keyUniverse; | ||
| 143 | vals = m.vals.clone(); | ||
| 144 | size = m.size; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Creates an enum map initialized from the specified map. If the | ||
| 149 | * specified map is an <tt>EnumMap</tt> instance, this constructor behaves | ||
| 150 | * identically to {@link #EnumMap(EnumMap)}. Otherwise, the specified map | ||
| 151 | * must contain at least one mapping (in order to determine the new | ||
| 152 | * enum map's key type). | ||
| 153 | * | ||
| 154 | * @param m the map from which to initialize this enum map | ||
| 155 | * @throws IllegalArgumentException if <tt>m</tt> is not an | ||
| 156 | * <tt>EnumMap</tt> instance and contains no mappings | ||
| 157 | * @throws NullPointerException if <tt>m</tt> is null | ||
| 158 | */ | ||
| 159 | public EnumMap(Map<K, ? extends V> m) { | ||
| 160 | if (m instanceof EnumMap) { | ||
| 161 | EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m; | ||
| 162 | keyType = em.keyType; | ||
| 163 | keyUniverse = em.keyUniverse; | ||
| 164 | vals = em.vals.clone(); | ||
| 165 | size = em.size; | ||
| 166 | } else { | ||
| 167 | if (m.isEmpty()) | ||
| 168 | throw new IllegalArgumentException("Specified map is empty"); | ||
| 169 | keyType = m.keySet().iterator().next().getDeclaringClass(); | ||
| 170 | keyUniverse = getKeyUniverse(keyType); | ||
| 171 | vals = new Object[keyUniverse.length]; | ||
| 172 | putAll(m); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | // Query Operations | ||
| 177 | |||
| 178 | /** | ||
| 179 | * Returns the number of key-value mappings in this map. | ||
| 180 | * | ||
| 181 | * @return the number of key-value mappings in this map | ||
| 182 | */ | ||
| 183 | public int size() { | ||
| 184 | return size; | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Returns <tt>true</tt> if this map maps one or more keys to the | ||
| 189 | * specified value. | ||
| 190 | * | ||
| 191 | * @param value the value whose presence in this map is to be tested | ||
| 192 | * @return <tt>true</tt> if this map maps one or more keys to this value | ||
| 193 | */ | ||
| 194 | public boolean containsValue(Object value) { | ||
| 195 | value = maskNull(value); | ||
| 196 | |||
| 197 | for (Object val : vals) | ||
| 198 | if (value.equals(val)) | ||
| 199 | return true; | ||
| 200 | |||
| 201 | return false; | ||
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Returns <tt>true</tt> if this map contains a mapping for the specified | ||
| 206 | * key. | ||
| 207 | * | ||
| 208 | * @param key the key whose presence in this map is to be tested | ||
| 209 | * @return <tt>true</tt> if this map contains a mapping for the specified | ||
| 210 | * key | ||
| 211 | */ | ||
| 212 | public boolean containsKey(Object key) { | ||
| 213 | return isValidKey(key) && vals[((Enum)key).ordinal()] != null; | ||
| 214 | } | ||
| 215 | |||
| 216 | private boolean containsMapping(Object key, Object value) { | ||
| 217 | return isValidKey(key) && | ||
| 218 | maskNull(value).equals(vals[((Enum)key).ordinal()]); | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Returns the value to which the specified key is mapped, | ||
| 223 | * or {@code null} if this map contains no mapping for the key. | ||
| 224 | * | ||
| 225 | * <p>More formally, if this map contains a mapping from a key | ||
| 226 | * {@code k} to a value {@code v} such that {@code (key == k)}, | ||
| 227 | * then this method returns {@code v}; otherwise it returns | ||
| 228 | * {@code null}. (There can be at most one such mapping.) | ||
| 229 | * | ||
| 230 | * <p>A return value of {@code null} does not <i>necessarily</i> | ||
| 231 | * indicate that the map contains no mapping for the key; it's also | ||
| 232 | * possible that the map explicitly maps the key to {@code null}. | ||
| 233 | * The {@link #containsKey containsKey} operation may be used to | ||
| 234 | * distinguish these two cases. | ||
| 235 | */ | ||
| 236 | public V get(Object key) { | ||
| 237 | return (isValidKey(key) ? | ||
| 238 | unmaskNull(vals[((Enum)key).ordinal()]) : null); | ||
| 239 | } | ||
| 240 | |||
| 241 | // Modification Operations | ||
| 242 | |||
| 243 | /** | ||
| 244 | * Associates the specified value with the specified key in this map. | ||
| 245 | * If the map previously contained a mapping for this key, the old | ||
| 246 | * value is replaced. | ||
| 247 | * | ||
| 248 | * @param key the key with which the specified value is to be associated | ||
| 249 | * @param value the value to be associated with the specified key | ||
| 250 | * | ||
| 251 | * @return the previous value associated with specified key, or | ||
| 252 | * <tt>null</tt> if there was no mapping for key. (A <tt>null</tt> | ||
| 253 | * return can also indicate that the map previously associated | ||
| 254 | * <tt>null</tt> with the specified key.) | ||
| 255 | * @throws NullPointerException if the specified key is null | ||
| 256 | */ | ||
| 257 | public V put(K key, V value) { | ||
| 258 | typeCheck(key); | ||
| 259 | |||
| 260 | int index = key.ordinal(); | ||
| 261 | Object oldValue = vals[index]; | ||
| 262 | vals[index] = maskNull(value); | ||
| 263 | if (oldValue == null) | ||
| 264 | size++; | ||
| 265 | return unmaskNull(oldValue); | ||
| 266 | } | ||
| 267 | |||
| 268 | /** | ||
| 269 | * Removes the mapping for this key from this map if present. | ||
| 270 | * | ||
| 271 | * @param key the key whose mapping is to be removed from the map | ||
| 272 | * @return the previous value associated with specified key, or | ||
| 273 | * <tt>null</tt> if there was no entry for key. (A <tt>null</tt> | ||
| 274 | * return can also indicate that the map previously associated | ||
| 275 | * <tt>null</tt> with the specified key.) | ||
| 276 | */ | ||
| 277 | public V remove(Object key) { | ||
| 278 | if (!isValidKey(key)) | ||
| 279 | return null; | ||
| 280 | int index = ((Enum)key).ordinal(); | ||
| 281 | Object oldValue = vals[index]; | ||
| 282 | vals[index] = null; | ||
| 283 | if (oldValue != null) | ||
| 284 | size--; | ||
| 285 | return unmaskNull(oldValue); | ||
| 286 | } | ||
| 287 | |||
| 288 | private boolean removeMapping(Object key, Object value) { | ||
| 289 | if (!isValidKey(key)) | ||
| 290 | return false; | ||
| 291 | int index = ((Enum)key).ordinal(); | ||
| 292 | if (maskNull(value).equals(vals[index])) { | ||
| 293 | vals[index] = null; | ||
| 294 | size--; | ||
| 295 | return true; | ||
| 296 | } | ||
| 297 | return false; | ||
| 298 | } | ||
| 299 | |||
| 300 | /** | ||
| 301 | * Returns true if key is of the proper type to be a key in this | ||
| 302 | * enum map. | ||
| 303 | */ | ||
| 304 | private boolean isValidKey(Object key) { | ||
| 305 | if (key == null) | ||
| 306 | return false; | ||
| 307 | |||
| 308 | // Cheaper than instanceof Enum followed by getDeclaringClass | ||
| 309 | Class keyClass = key.getClass(); | ||
| 310 | return keyClass == keyType || keyClass.getSuperclass() == keyType; | ||
| 311 | } | ||
| 312 | |||
| 313 | // Bulk Operations | ||
| 314 | |||
| 315 | /** | ||
| 316 | * Copies all of the mappings from the specified map to this map. | ||
| 317 | * These mappings will replace any mappings that this map had for | ||
| 318 | * any of the keys currently in the specified map. | ||
| 319 | * | ||
| 320 | * @param m the mappings to be stored in this map | ||
| 321 | * @throws NullPointerException the specified map is null, or if | ||
| 322 | * one or more keys in the specified map are null | ||
| 323 | */ | ||
| 324 | public void putAll(Map<? extends K, ? extends V> m) { | ||
| 325 | if (m instanceof EnumMap) { | ||
| 326 | EnumMap<? extends K, ? extends V> em = | ||
| 327 | (EnumMap<? extends K, ? extends V>)m; | ||
| 328 | if (em.keyType != keyType) { | ||
| 329 | if (em.isEmpty()) | ||
| 330 | return; | ||
| 331 | throw new ClassCastException(em.keyType + " != " + keyType); | ||
| 332 | } | ||
| 333 | |||
| 334 | for (int i = 0; i < keyUniverse.length; i++) { | ||
| 335 | Object emValue = em.vals[i]; | ||
| 336 | if (emValue != null) { | ||
| 337 | if (vals[i] == null) | ||
| 338 | size++; | ||
| 339 | vals[i] = emValue; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } else { | ||
| 343 | super.putAll(m); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Removes all mappings from this map. | ||
| 349 | */ | ||
| 350 | public void clear() { | ||
| 351 | Arrays.fill(vals, null); | ||
| 352 | size = 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | // Views | ||
| 356 | |||
| 357 | /** | ||
| 358 | * This field is initialized to contain an instance of the entry set | ||
| 359 | * view the first time this view is requested. The view is stateless, | ||
| 360 | * so there's no reason to create more than one. | ||
| 361 | */ | ||
| 362 | private transient Set<Map.Entry<K,V>> entrySet = null; | ||
| 363 | |||
| 364 | /** | ||
| 365 | * Returns a {@link Set} view of the keys contained in this map. | ||
| 366 | * The returned set obeys the general contract outlined in | ||
| 367 | * {@link Map#keySet()}. The set's iterator will return the keys | ||
| 368 | * in their natural order (the order in which the enum constants | ||
| 369 | * are declared). | ||
| 370 | * | ||
| 371 | * @return a set view of the keys contained in this enum map | ||
| 372 | */ | ||
| 373 | public Set<K> keySet() { | ||
| 374 | Set<K> ks = keySet; | ||
| 375 | if (ks != null) | ||
| 376 | return ks; | ||
| 377 | else | ||
| 378 | return keySet = new KeySet(); | ||
| 379 | } | ||
| 380 | |||
| 381 | private class KeySet extends AbstractSet<K> { | ||
| 382 | public Iterator<K> iterator() { | ||
| 383 | return new KeyIterator(); | ||
| 384 | } | ||
| 385 | public int size() { | ||
| 386 | return size; | ||
| 387 | } | ||
| 388 | public boolean contains(Object o) { | ||
| 389 | return containsKey(o); | ||
| 390 | } | ||
| 391 | public boolean remove(Object o) { | ||
| 392 | int oldSize = size; | ||
| 393 | EnumMap.this.remove(o); | ||
| 394 | return size != oldSize; | ||
| 395 | } | ||
| 396 | public void clear() { | ||
| 397 | EnumMap.this.clear(); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Returns a {@link Collection} view of the values contained in this map. | ||
| 403 | * The returned collection obeys the general contract outlined in | ||
| 404 | * {@link Map#values()}. The collection's iterator will return the | ||
| 405 | * values in the order their corresponding keys appear in map, | ||
| 406 | * which is their natural order (the order in which the enum constants | ||
| 407 | * are declared). | ||
| 408 | * | ||
| 409 | * @return a collection view of the values contained in this map | ||
| 410 | */ | ||
| 411 | public Collection<V> values() { | ||
| 412 | Collection<V> vs = values; | ||
| 413 | if (vs != null) | ||
| 414 | return vs; | ||
| 415 | else | ||
| 416 | return values = new Values(); | ||
| 417 | } | ||
| 418 | |||
| 419 | private class Values extends AbstractCollection<V> { | ||
| 420 | public Iterator<V> iterator() { | ||
| 421 | return new ValueIterator(); | ||
| 422 | } | ||
| 423 | public int size() { | ||
| 424 | return size; | ||
| 425 | } | ||
| 426 | public boolean contains(Object o) { | ||
| 427 | return containsValue(o); | ||
| 428 | } | ||
| 429 | public boolean remove(Object o) { | ||
| 430 | o = maskNull(o); | ||
| 431 | |||
| 432 | for (int i = 0; i < vals.length; i++) { | ||
| 433 | if (o.equals(vals[i])) { | ||
| 434 | vals[i] = null; | ||
| 435 | size--; | ||
| 436 | return true; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | return false; | ||
| 440 | } | ||
| 441 | public void clear() { | ||
| 442 | EnumMap.this.clear(); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | /** | ||
| 447 | * Returns a {@link Set} view of the mappings contained in this map. | ||
| 448 | * The returned set obeys the general contract outlined in | ||
| 449 | * {@link Map#keySet()}. The set's iterator will return the | ||
| 450 | * mappings in the order their keys appear in map, which is their | ||
| 451 | * natural order (the order in which the enum constants are declared). | ||
| 452 | * | ||
| 453 | * @return a set view of the mappings contained in this enum map | ||
| 454 | */ | ||
| 455 | public Set<Map.Entry<K,V>> entrySet() { | ||
| 456 | Set<Map.Entry<K,V>> es = entrySet; | ||
| 457 | if (es != null) | ||
| 458 | return es; | ||
| 459 | else | ||
| 460 | return entrySet = new EntrySet(); | ||
| 461 | } | ||
| 462 | |||
| 463 | private class EntrySet extends AbstractSet<Map.Entry<K,V>> { | ||
| 464 | public Iterator<Map.Entry<K,V>> iterator() { | ||
| 465 | return new EntryIterator(); | ||
| 466 | } | ||
| 467 | |||
| 468 | public boolean contains(Object o) { | ||
| 469 | if (!(o instanceof Map.Entry)) | ||
| 470 | return false; | ||
| 471 | Map.Entry entry = (Map.Entry)o; | ||
| 472 | return containsMapping(entry.getKey(), entry.getValue()); | ||
| 473 | } | ||
| 474 | public boolean remove(Object o) { | ||
| 475 | if (!(o instanceof Map.Entry)) | ||
| 476 | return false; | ||
| 477 | Map.Entry entry = (Map.Entry)o; | ||
| 478 | return removeMapping(entry.getKey(), entry.getValue()); | ||
| 479 | } | ||
| 480 | public int size() { | ||
| 481 | return size; | ||
| 482 | } | ||
| 483 | public void clear() { | ||
| 484 | EnumMap.this.clear(); | ||
| 485 | } | ||
| 486 | public Object[] toArray() { | ||
| 487 | return fillEntryArray(new Object[size]); | ||
| 488 | } | ||
| 489 | @SuppressWarnings("unchecked") | ||
| 490 | public <T> T[] toArray(T[] a) { | ||
| 491 | int size = size(); | ||
| 492 | if (a.length < size) | ||
| 493 | a = (T[])java.lang.reflect.Array | ||
| 494 | .newInstance(a.getClass().getComponentType(), size); | ||
| 495 | if (a.length > size) | ||
| 496 | a[size] = null; | ||
| 497 | return (T[]) fillEntryArray(a); | ||
| 498 | } | ||
| 499 | private Object[] fillEntryArray(Object[] a) { | ||
| 500 | int j = 0; | ||
| 501 | for (int i = 0; i < vals.length; i++) | ||
| 502 | if (vals[i] != null) | ||
| 503 | a[j++] = new AbstractMap.SimpleEntry<>( | ||
| 504 | keyUniverse[i], unmaskNull(vals[i])); | ||
| 505 | return a; | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | private abstract class EnumMapIterator<T> implements Iterator<T> { | ||
| 510 | // Lower bound on index of next element to return | ||
| 511 | int index = 0; | ||
| 512 | |||
| 513 | // Index of last returned element, or -1 if none | ||
| 514 | int lastReturnedIndex = -1; | ||
| 515 | |||
| 516 | public boolean hasNext() { | ||
| 517 | while (index < vals.length && vals[index] == null) | ||
| 518 | index++; | ||
| 519 | return index != vals.length; | ||
| 520 | } | ||
| 521 | |||
| 522 | public void remove() { | ||
| 523 | checkLastReturnedIndex(); | ||
| 524 | |||
| 525 | if (vals[lastReturnedIndex] != null) { | ||
| 526 | vals[lastReturnedIndex] = null; | ||
| 527 | size--; | ||
| 528 | } | ||
| 529 | lastReturnedIndex = -1; | ||
| 530 | } | ||
| 531 | |||
| 532 | private void checkLastReturnedIndex() { | ||
| 533 | if (lastReturnedIndex < 0) | ||
| 534 | throw new IllegalStateException(); | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | private class KeyIterator extends EnumMapIterator<K> { | ||
| 539 | public K next() { | ||
| 540 | if (!hasNext()) | ||
| 541 | throw new NoSuchElementException(); | ||
| 542 | lastReturnedIndex = index++; | ||
| 543 | return keyUniverse[lastReturnedIndex]; | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | private class ValueIterator extends EnumMapIterator<V> { | ||
| 548 | public V next() { | ||
| 549 | if (!hasNext()) | ||
| 550 | throw new NoSuchElementException(); | ||
| 551 | lastReturnedIndex = index++; | ||
| 552 | return unmaskNull(vals[lastReturnedIndex]); | ||
| 553 | } | ||
| 554 | } | ||
| 555 | |||
| 556 | private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> { | ||
| 557 | private Entry lastReturnedEntry = null; | ||
| 558 | |||
| 559 | public Map.Entry<K,V> next() { | ||
| 560 | if (!hasNext()) | ||
| 561 | throw new NoSuchElementException(); | ||
| 562 | lastReturnedEntry = new Entry(index++); | ||
| 563 | return lastReturnedEntry; | ||
| 564 | } | ||
| 565 | |||
| 566 | public void remove() { | ||
| 567 | lastReturnedIndex = | ||
| 568 | ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index); | ||
| 569 | super.remove(); | ||
| 570 | lastReturnedEntry.index = lastReturnedIndex; | ||
| 571 | lastReturnedEntry = null; | ||
| 572 | } | ||
| 573 | |||
| 574 | private class Entry implements Map.Entry<K,V> { | ||
| 575 | private int index; | ||
| 576 | |||
| 577 | private Entry(int index) { | ||
| 578 | this.index = index; | ||
| 579 | } | ||
| 580 | |||
| 581 | public K getKey() { | ||
| 582 | checkIndexForEntryUse(); | ||
| 583 | return keyUniverse[index]; | ||
| 584 | } | ||
| 585 | |||
| 586 | public V getValue() { | ||
| 587 | checkIndexForEntryUse(); | ||
| 588 | return unmaskNull(vals[index]); | ||
| 589 | } | ||
| 590 | |||
| 591 | public V setValue(V value) { | ||
| 592 | checkIndexForEntryUse(); | ||
| 593 | V oldValue = unmaskNull(vals[index]); | ||
| 594 | vals[index] = maskNull(value); | ||
| 595 | return oldValue; | ||
| 596 | } | ||
| 597 | |||
| 598 | public boolean equals(Object o) { | ||
| 599 | if (index < 0) | ||
| 600 | return o == this; | ||
| 601 | |||
| 602 | if (!(o instanceof Map.Entry)) | ||
| 603 | return false; | ||
| 604 | |||
| 605 | Map.Entry e = (Map.Entry)o; | ||
| 606 | V ourValue = unmaskNull(vals[index]); | ||
| 607 | Object hisValue = e.getValue(); | ||
| 608 | return (e.getKey() == keyUniverse[index] && | ||
| 609 | (ourValue == hisValue || | ||
| 610 | (ourValue != null && ourValue.equals(hisValue)))); | ||
| 611 | } | ||
| 612 | |||
| 613 | public int hashCode() { | ||
| 614 | if (index < 0) | ||
| 615 | return super.hashCode(); | ||
| 616 | |||
| 617 | return entryHashCode(index); | ||
| 618 | } | ||
| 619 | |||
| 620 | public String toString() { | ||
| 621 | if (index < 0) | ||
| 622 | return super.toString(); | ||
| 623 | |||
| 624 | return keyUniverse[index] + "=" | ||
| 625 | + unmaskNull(vals[index]); | ||
| 626 | } | ||
| 627 | |||
| 628 | private void checkIndexForEntryUse() { | ||
| 629 | if (index < 0) | ||
| 630 | throw new IllegalStateException("Entry was removed"); | ||
| 631 | } | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | // Comparison and hashing | ||
| 636 | |||
| 637 | /** | ||
| 638 | * Compares the specified object with this map for equality. Returns | ||
| 639 | * <tt>true</tt> if the given object is also a map and the two maps | ||
| 640 | * represent the same mappings, as specified in the {@link | ||
| 641 | * Map#equals(Object)} contract. | ||
| 642 | * | ||
| 643 | * @param o the object to be compared for equality with this map | ||
| 644 | * @return <tt>true</tt> if the specified object is equal to this map | ||
| 645 | */ | ||
| 646 | public boolean equals(Object o) { | ||
| 647 | if (this == o) | ||
| 648 | return true; | ||
| 649 | if (o instanceof EnumMap) | ||
| 650 | return equals((EnumMap)o); | ||
| 651 | if (!(o instanceof Map)) | ||
| 652 | return false; | ||
| 653 | |||
| 654 | Map<K,V> m = (Map<K,V>)o; | ||
| 655 | if (size != m.size()) | ||
| 656 | return false; | ||
| 657 | |||
| 658 | for (int i = 0; i < keyUniverse.length; i++) { | ||
| 659 | if (null != vals[i]) { | ||
| 660 | K key = keyUniverse[i]; | ||
| 661 | V value = unmaskNull(vals[i]); | ||
| 662 | if (null == value) { | ||
| 663 | if (!((null == m.get(key)) && m.containsKey(key))) | ||
| 664 | return false; | ||
| 665 | } else { | ||
| 666 | if (!value.equals(m.get(key))) | ||
| 667 | return false; | ||
| 668 | } | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 672 | return true; | ||
| 673 | } | ||
| 674 | |||
| 675 | private boolean equals(EnumMap em) { | ||
| 676 | if (em.keyType != keyType) | ||
| 677 | return size == 0 && em.size == 0; | ||
| 678 | |||
| 679 | // Key types match, compare each value | ||
| 680 | for (int i = 0; i < keyUniverse.length; i++) { | ||
| 681 | Object ourValue = vals[i]; | ||
| 682 | Object hisValue = em.vals[i]; | ||
| 683 | if (hisValue != ourValue && | ||
| 684 | (hisValue == null || !hisValue.equals(ourValue))) | ||
| 685 | return false; | ||
| 686 | } | ||
| 687 | return true; | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * Returns the hash code value for this map. The hash code of a map is | ||
| 692 | * defined to be the sum of the hash codes of each entry in the map. | ||
| 693 | */ | ||
| 694 | public int hashCode() { | ||
| 695 | int h = 0; | ||
| 696 | |||
| 697 | for (int i = 0; i < keyUniverse.length; i++) { | ||
| 698 | if (null != vals[i]) { | ||
| 699 | h += entryHashCode(i); | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | return h; | ||
| 704 | } | ||
| 705 | |||
| 706 | private int entryHashCode(int index) { | ||
| 707 | return (keyUniverse[index].hashCode() ^ vals[index].hashCode()); | ||
| 708 | } | ||
| 709 | |||
| 710 | /** | ||
| 711 | * Returns a shallow copy of this enum map. (The values themselves | ||
| 712 | * are not cloned. | ||
| 713 | * | ||
| 714 | * @return a shallow copy of this enum map | ||
| 715 | */ | ||
| 716 | public EnumMap<K, V> clone() { | ||
| 717 | EnumMap<K, V> result = null; | ||
| 718 | try { | ||
| 719 | result = (EnumMap<K, V>) super.clone(); | ||
| 720 | } catch(CloneNotSupportedException e) { | ||
| 721 | throw new AssertionError(); | ||
| 722 | } | ||
| 723 | result.vals = result.vals.clone(); | ||
| 724 | return result; | ||
| 725 | } | ||
| 726 | |||
| 727 | /** | ||
| 728 | * Throws an exception if e is not of the correct type for this enum set. | ||
| 729 | */ | ||
| 730 | private void typeCheck(K key) { | ||
| 731 | Class keyClass = key.getClass(); | ||
| 732 | if (keyClass != keyType && keyClass.getSuperclass() != keyType) | ||
| 733 | throw new ClassCastException(keyClass + " != " + keyType); | ||
| 734 | } | ||
| 735 | |||
| 736 | /** | ||
| 737 | * Returns all of the values comprising K. | ||
| 738 | * The result is uncloned, cached, and shared by all callers. | ||
| 739 | */ | ||
| 740 | private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) { | ||
| 741 | return SharedSecrets.getJavaLangAccess() | ||
| 742 | .getEnumConstantsShared(keyType); | ||
| 743 | } | ||
| 744 | |||
| 745 | private static final long serialVersionUID = 458661240069192865L; | ||
| 746 | |||
| 747 | /** | ||
| 748 | * Save the state of the <tt>EnumMap</tt> instance to a stream (i.e., | ||
| 749 | * serialize it). | ||
| 750 | * | ||
| 751 | * @serialData The <i>size</i> of the enum map (the number of key-value | ||
| 752 | * mappings) is emitted (int), followed by the key (Object) | ||
| 753 | * and value (Object) for each key-value mapping represented | ||
| 754 | * by the enum map. | ||
| 755 | */ | ||
| 756 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 757 | throws java.io.IOException | ||
| 758 | { | ||
| 759 | // Write out the key type and any hidden stuff | ||
| 760 | s.defaultWriteObject(); | ||
| 761 | |||
| 762 | // Write out size (number of Mappings) | ||
| 763 | s.writeInt(size); | ||
| 764 | |||
| 765 | // Write out keys and values (alternating) | ||
| 766 | int entriesToBeWritten = size; | ||
| 767 | for (int i = 0; entriesToBeWritten > 0; i++) { | ||
| 768 | if (null != vals[i]) { | ||
| 769 | s.writeObject(keyUniverse[i]); | ||
| 770 | s.writeObject(unmaskNull(vals[i])); | ||
| 771 | entriesToBeWritten--; | ||
| 772 | } | ||
| 773 | } | ||
| 774 | } | ||
| 775 | |||
| 776 | /** | ||
| 777 | * Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e., | ||
| 778 | * deserialize it). | ||
| 779 | */ | ||
| 780 | private void readObject(java.io.ObjectInputStream s) | ||
| 781 | throws java.io.IOException, ClassNotFoundException | ||
| 782 | { | ||
| 783 | // Read in the key type and any hidden stuff | ||
| 784 | s.defaultReadObject(); | ||
| 785 | |||
| 786 | keyUniverse = getKeyUniverse(keyType); | ||
| 787 | vals = new Object[keyUniverse.length]; | ||
| 788 | |||
| 789 | // Read in size (number of Mappings) | ||
| 790 | int size = s.readInt(); | ||
| 791 | |||
| 792 | // Read the keys and values, and put the mappings in the HashMap | ||
| 793 | for (int i = 0; i < size; i++) { | ||
| 794 | K key = (K) s.readObject(); | ||
| 795 | V value = (V) s.readObject(); | ||
| 796 | put(key, value); | ||
| 797 | } | ||
| 798 | } | ||
| 799 | } | ||