| /******************************************************************************* |
| |
| @file HashMap.d |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for damages |
| of any kind arising from the use of this software. |
| |
| Permission is hereby granted to anyone to use this software for any |
| purpose, including commercial applications, and to alter it and/or |
| redistribute it freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must |
| not claim that you wrote the original software. If you use this |
| software in a product, an acknowledgment within documentation of |
| said product would be appreciated but is not required. |
| |
| 2. Altered source versions must be plainly marked as such, and must |
| not be misrepresented as being the original software. |
| |
| 3. This notice may not be removed or altered from any distribution |
| of the source. |
| |
| 4. Derivative works are permitted, but they must carry this notice |
| in full and credit the original source. |
| |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| |
| Written by Doug Lea with assistance from members of JCP JSR-166 |
| Expert Group and released to the public domain, as explained at |
| http://creativecommons.org/licenses/publicdomain |
| |
| @version Initial version, July 2004 |
| @author Doug Lea; ported/modified by Kris |
| |
| *******************************************************************************/ |
| |
| module mango.cache.HashMap; |
| |
| /****************************************************************************** |
| |
| ******************************************************************************/ |
| |
| extern (C) |
| { |
| int memcmp(char *, char *, uint); |
| } |
| |
| |
| /** |
| * A hash table supporting full concurrency of retrievals and |
| * adjustable expected concurrency for updates. This class obeys the |
| * same functional specification as {@link java.util.Hashtable}, and |
| * includes versions of methods corresponding to each method of |
| * <tt>Hashtable</tt>. However, even though all operations are |
| * thread-safe, retrieval operations do <em>not</em> entail locking, |
| * and there is <em>not</em> any support for locking the entire table |
| * in a way that prevents all access. This class is fully |
| * interoperable with <tt>Hashtable</tt> in programs that rely on its |
| * thread safety but not on its synchronization details. |
| * |
| * <p> Retrieval operations (including <tt>get</tt>) generally do not |
| * block, so may overlap with update operations (including |
| * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results |
| * of the most recently <em>completed</em> update operations holding |
| * upon their onset. For aggregate operations such as <tt>putAll</tt> |
| * and <tt>clear</tt>, concurrent retrievals may reflect insertion or |
| * removal of only some entries. Similarly, Iterators and |
| * Enumerations return elements reflecting the state of the hash table |
| * at some point at or since the creation of the iterator/enumeration. |
| * They do <em>not</em> throw |
| * {@link ConcurrentModificationException}. However, iterators are |
| * designed to be used by only one thread at a time. |
| * |
| * <p> The allowed concurrency among update operations is guided by |
| * the optional <tt>concurrencyLevel</tt> constructor argument |
| * (default 16), which is used as a hint for internal sizing. The |
| * table is internally partitioned to try to permit the indicated |
| * number of concurrent updates without contention. Because placement |
| * in hash tables is essentially random, the actual concurrency will |
| * vary. Ideally, you should choose a value to accommodate as many |
| * threads as will ever concurrently modify the table. Using a |
| * significantly higher value than you need can waste space and time, |
| * and a significantly lower value can lead to thread contention. But |
| * overestimates and underestimates within an order of magnitude do |
| * not usually have much noticeable impact. A value of one is |
| * appropriate when it is known that only one thread will modify and |
| * all others will only read. Also, resizing this or any other kind of |
| * hash table is a relatively slow operation, so, when possible, it is |
| * a good idea to provide estimates of expected table sizes in |
| * constructors. |
| * |
| * <p>This class and its views and iterators implement all of the |
| * <em>optional</em> methods of the {@link Map} and {@link Iterator} |
| * interfaces. |
| * |
| * <p> Like {@link java.util.Hashtable} but unlike {@link |
| * java.util.HashMap}, this class does NOT allow <tt>null</tt> to be |
| * used as a key or value. |
| * |
| * <p>This class is a member of the |
| * <a href="{@docRoot}/../guide/collections/index.html"> |
| * Java Collections Framework</a>. |
| * |
| * @since 1.5 |
| * @author Doug Lea |
| * @param <K> the type of keys maintained by this map |
| * @param <V> the type of mapped values |
| */ |
| |
| class HashMap |
| { |
| alias void[] K; |
| alias Object V; |
| alias jhash hash; // jhash, fnv, or walter |
| |
| /* |
| * The basic strategy is to subdivide the table among Segments, |
| * each of which itself is a concurrently readable hash table. |
| */ |
| |
| /* ---------------- Constants -------------- */ |
| |
| /** |
| * The default initial number of table slots for this table. |
| * Used when not otherwise specified in constructor. |
| */ |
| private const uint DEFAULT_INITIAL_CAPACITY = 16; |
| |
| /** |
| * The maximum capacity, used if a higher value is implicitly |
| * specified by either of the constructors with arguments. MUST |
| * be a power of two <= 1<<30 to ensure that entries are indexible |
| * using ints. |
| */ |
| private const uint MAXIMUM_CAPACITY = 1 << 30; |
| |
| /** |
| * The default load factor for this table. Used when not |
| * otherwise specified in constructor. |
| */ |
| private const float DEFAULT_LOAD_FACTOR = 0.75f; |
| |
| /** |
| * The default number of concurrency control segments. |
| **/ |
| private const uint DEFAULT_SEGMENTS = 16; |
| |
| /** |
| * The maximum number of segments to allow; used to bound |
| * constructor arguments. |
| */ |
| private const uint MAX_SEGMENTS = 1 << 16; // slightly conservative |
| |
| |
| /* ---------------- Fields -------------- */ |
| |
| /** |
| * Mask value for indexing into segments. The upper bits of a |
| * key's hash code are used to choose the segment. |
| **/ |
| private final int segmentMask; |
| |
| /** |
| * Shift value for indexing within segments. |
| **/ |
| private final int segmentShift; |
| |
| /** |
| * The segments, each of which is a specialized hash table |
| */ |
| private final Segment[] segments; |
| |
| |
| /* ---------------- Small Utilities -------------- */ |
| |
| /** |
| * Returns a hash code for non-null Object x. |
| * Uses the same hash code spreader as most other java.util hash tables. |
| * @param x the object serving as a key |
| * @return the hash code |
| */ |
| private static final uint walter(K x) |
| { |
| uint h = typeid(char[]).getHash(&x); |
| |
| h += ~(h << 9); |
| h ^= (h >>> 14); |
| h += (h << 4); |
| h ^= (h >>> 10); |
| return h; |
| } |
| |
| /** |
| * Returns a hash code for non-null Object x. |
| * uses the FNV hash function |
| * @param x the object serving as a key |
| * @return the hash code |
| */ |
| private static final uint fnv(K x) |
| { |
| uint hash = 2_166_136_261; |
| |
| foreach (ubyte c; cast(ubyte[])x) |
| { |
| hash ^= c; |
| hash *= 16_777_619; |
| } |
| return hash; |
| } |
| |
| |
| |
| /** |
| * hash() -- hash a variable-length key into a 32-bit value |
| * k : the key (the unaligned variable-length array of bytes) |
| * len : the length of the key, counting by bytes |
| * level : can be any 4-byte value |
| * Returns a 32-bit value. Every bit of the key affects every bit of |
| * the return value. Every 1-bit and 2-bit delta achieves avalanche. |
| * About 36+6len instructions. |
| * |
| * The best hash table sizes are powers of 2. There is no need to do |
| * mod a prime (mod is sooo slow!). If you need less than 32 bits, |
| * use a bitmask. For example, if you need only 10 bits, do |
| * h = (h & hashmask(10)); |
| * In which case, the hash table should have hashsize(10) elements. |
| * |
| * If you are hashing n strings (ub1 **)k, do it like this: |
| * for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h); |
| * |
| * By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this |
| * code any way you wish, private, educational, or commercial. It's free. |
| * |
| * See http://burlteburtle.net/bob/hash/evahash.html |
| * Use for hash table lookup, or anything where one collision in 2^32 is |
| * acceptable. Do NOT use for cryptographic purposes. |
| */ |
| |
| static final uint jhash(K x) |
| { |
| ubyte *k; |
| uint a, |
| b, |
| c, |
| len; |
| |
| len = x.length; |
| k = cast(ubyte *)x; |
| a = b = 0x9e3779b9; |
| |
| // the previous hash value |
| c = 0; |
| |
| // handle most of the key |
| while (len >= 12) |
| { |
| a += *cast(uint *)(k + 0); |
| b += *cast(uint *)(k + 4); |
| c += *cast(uint *)(k + 8); |
| |
| a -= b; a -= c; a ^= (c >> 13); |
| b -= c; b -= a; b ^= (a << 8); |
| c -= a; c -= b; c ^= (b >> 13); |
| a -= b; a -= c; a ^= (c >> 12); |
| b -= c; b -= a; b ^= (a << 16); |
| c -= a; c -= b; c ^= (b >> 5); |
| a -= b; a -= c; a ^= (c >> 3); |
| b -= c; b -= a; b ^= (a << 10); |
| c -= a; c -= b; c ^= (b >> 15); |
| k += 12; len -= 12; |
| } |
| |
| // handle the last 11 bytes |
| c += x.length; |
| switch (len) |
| { |
| case 11: c += (cast(uint)k[10] << 24); |
| |
| case 10: c += (cast(uint)k[9] << 16); |
| |
| case 9: c += (cast(uint)k[8] << 8); |
| |
| case 8: b += (cast(uint)k[7] << 24); |
| |
| case 7: b += (cast(uint)k[6] << 16); |
| |
| case 6: b += (cast(uint)k[5] << 8); |
| |
| case 5: b += k[4]; |
| |
| case 4: a += (cast(uint)k[3] << 24); |
| |
| case 3: a += (cast(uint)k[2] << 16); |
| |
| case 2: a += (cast(uint)k[1] << 8); |
| |
| case 1: a += k[0]; |
| |
| default: |
| } |
| |
| a -= b; a -= c; a ^= (c >> 13); |
| b -= c; b -= a; b ^= (a << 8); |
| c -= a; c -= b; c ^= (b >> 13); |
| a -= b; a -= c; a ^= (c >> 12); |
| b -= c; b -= a; b ^= (a << 16); |
| c -= a; c -= b; c ^= (b >> 5); |
| a -= b; a -= c; a ^= (c >> 3); |
| b -= c; b -= a; b ^= (a << 10); |
| c -= a; c -= b; c ^= (b >> 15); |
| |
| return c; |
| } |
| |
| |
| /** |
| * Returns the segment that should be used for key with given hash |
| * @param hash the hash code for the key |
| * @return the segment |
| */ |
| private final Segment segmentFor(uint hash) |
| { |
| return segments[(hash >>> segmentShift) & segmentMask]; |
| } |
| |
| /* ---------------- Inner Classes -------------- */ |
| |
| /** |
| * ConcurrentHashMap list entry. Note that this is never exported |
| * out as a user-visible Map.Entry. |
| * |
| * Because the value field is volatile, not final, it is legal wrt |
| * the Java Memory Model for an unsynchronized reader to see null |
| * instead of initial value when read via a data race. Although a |
| * reordering leading to this is not likely to ever actually |
| * occur, the Segment.readValueUnderLock method is used as a |
| * backup in case a null (pre-initialized) value is ever seen in |
| * an unsynchronized access method. |
| */ |
| private static class HashEntry |
| { |
| final K key; |
| final uint hash; |
| final V value; |
| final HashEntry next; |
| |
| this(K key, uint hash, HashEntry next, V value) |
| { |
| this.key = key; |
| this.hash = hash; |
| this.next = next; |
| this.value = value; |
| } |
| } |
| |
| /** |
| * Segments are specialized versions of hash tables. This |
| * subclasses from ReentrantLock opportunistically, just to |
| * simplify some locking and avoid separate construction. |
| **/ |
| static class Segment |
| { |
| /* |
| * Segments maintain a table of entry lists that are ALWAYS |
| * kept in a consistent state, so can be read without locking. |
| * Next fields of nodes are immutable (final). All list |
| * additions are performed at the front of each bin. This |
| * makes it easy to check changes, and also fast to traverse. |
| * When nodes would otherwise be changed, new nodes are |
| * created to replace them. This works well for hash tables |
| * since the bin lists tend to be short. (The average length |
| * is less than two for the default load factor threshold.) |
| * |
| * Read operations can thus proceed without locking, but rely |
| * on selected uses of volatiles to ensure that completed |
| * write operations performed by other threads are |
| * noticed. For most purposes, the "count" field, tracking the |
| * number of elements, serves as that volatile variable |
| * ensuring visibility. This is convenient because this field |
| * needs to be read in many read operations anyway: |
| * |
| * - All (unsynchronized) read operations must first read the |
| * "count" field, and should not look at table entries if |
| * it is 0. |
| * |
| * - All (synchronized) write operations should write to |
| * the "count" field after structurally changing any bin. |
| * The operations must not take any action that could even |
| * momentarily cause a concurrent read operation to see |
| * inconsistent data. This is made easier by the nature of |
| * the read operations in Map. For example, no operation |
| * can reveal that the table has grown but the threshold |
| * has not yet been updated, so there are no atomicity |
| * requirements for this with respect to reads. |
| * |
| * As a guide, all critical volatile reads and writes to the |
| * count field are marked in code comments. |
| */ |
| |
| /** |
| * The number of elements in this segment's region. |
| **/ |
| int count; |
| |
| /** |
| * The table is rehashed when its size exceeds this threshold. |
| * (The value of this field is always (int)(capacity * |
| * loadFactor).) |
| */ |
| int threshold; |
| |
| /** |
| * The per-segment table. Declared as a raw type, casted |
| * to HashEntry<K,V> on each use. |
| */ |
| HashEntry[] table; |
| |
| /** |
| * The load factor for the hash table. Even though this value |
| * is same for all segments, it is replicated to avoid needing |
| * links to outer object. |
| * @serial |
| */ |
| final float loadFactor; |
| |
| this(int initialCapacity, float lf) |
| { |
| loadFactor = lf; |
| setTable(new HashEntry[initialCapacity]); |
| } |
| |
| /** |
| * Set table to new HashEntry array. |
| * Call only while holding lock or in constructor. |
| **/ |
| private final void setTable(HashEntry[] newTable) |
| { |
| threshold = cast(int)(newTable.length * loadFactor); |
| volatile table = newTable; |
| } |
| |
| /** |
| * Return properly casted first entry of bin for given hash |
| */ |
| private final HashEntry getFirst(uint hash) |
| { |
| HashEntry[] tab; |
| |
| volatile tab = table; |
| return tab[hash & (tab.length - 1)]; |
| } |
| |
| /** |
| * Return true if the two keys match |
| */ |
| private static final bool matchKey(K a, K b) |
| { |
| if (a.length == b.length) |
| return cast(bool)(memcmp(cast(char *)a, cast(char *)b, a.length) == 0); |
| |
| return false; |
| } |
| |
| /* Specialized implementations of map methods */ |
| |
| final V get(K key, uint hash) |
| { |
| int c; |
| |
| // read-volatile |
| volatile c = count; |
| if (c) |
| { |
| HashEntry e = getFirst(hash); |
| while (e) |
| { |
| if (hash == e.hash && matchKey(key, e.key)) |
| { |
| V v; |
| volatile v = e.value; |
| if (v) |
| return v; |
| |
| synchronized (this) |
| return e.value; |
| } |
| e = e.next; |
| } |
| } |
| return null; |
| } |
| |
| |
| final bool containsKey(K key, uint hash) |
| { |
| int c; |
| |
| // read-volatile |
| volatile c = count; |
| if (c) |
| { |
| HashEntry e = getFirst(hash); |
| while (e) |
| { |
| if (e.hash == hash && matchKey(key, e.key)) |
| return true; |
| |
| e = e.next; |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| final synchronized V replace(K key, uint hash, V newValue) |
| { |
| HashEntry e = getFirst(hash); |
| |
| while (e && (e.hash != hash || !matchKey(key, e.key))) |
| e = e.next; |
| |
| V oldValue = null; |
| if (e) |
| volatile |
| { |
| oldValue = e.value; |
| e.value = newValue; |
| } |
| return oldValue; |
| } |
| |
| |
| final synchronized V put(K key, uint hash, V value, bool onlyIfAbsent) |
| { |
| int c; |
| |
| volatile c = count; |
| if (c++ > threshold) |
| rehash(); |
| |
| HashEntry[] tab; |
| volatile tab = table; |
| uint index = hash & (tab.length - 1); |
| HashEntry first = tab[index]; |
| HashEntry e = first; |
| |
| while (e && (e.hash != hash || !matchKey(key, e.key))) |
| e = e.next; |
| |
| V oldValue; |
| if (e) |
| { |
| volatile oldValue = e.value; |
| if (!onlyIfAbsent) |
| volatile e.value = value; |
| } |
| else |
| { |
| oldValue = null; |
| tab[index] = new HashEntry(key, hash, first, value); |
| |
| // write-volatile |
| volatile count = c; |
| } |
| return oldValue; |
| } |
| |
| |
| private final void rehash() |
| { |
| HashEntry[] oldTable; |
| |
| volatile oldTable = table; |
| int oldCapacity = oldTable.length; |
| if (oldCapacity >= MAXIMUM_CAPACITY) |
| return; |
| |
| /* |
| * Reclassify nodes in each list to new Map. Because we are |
| * using power-of-two expansion, the elements from each bin |
| * must either stay at same index, or move with a power of two |
| * offset. We eliminate unnecessary node creation by catching |
| * cases where old nodes can be reused because their next |
| * fields won't change. Statistically, at the default |
| * threshold, only about one-sixth of them need cloning when |
| * a table doubles. The nodes they replace will be garbage |
| * collectable as soon as they are no longer referenced by any |
| * reader thread that may be in the midst of traversing table |
| * right now. |
| */ |
| |
| HashEntry[] newTable = new HashEntry[oldCapacity << 1]; |
| threshold = cast(int)(newTable.length * loadFactor); |
| int sizeMask = newTable.length - 1; |
| |
| for (int i = 0; i < oldCapacity; ++i) |
| { |
| // We need to guarantee that any existing reads of old Map can |
| // proceed. So we cannot yet null out each bin. |
| HashEntry e = oldTable[i]; |
| |
| if (e) |
| { |
| HashEntry next = e.next; |
| uint idx = e.hash & sizeMask; |
| |
| // Single node on list |
| if (next is null) |
| newTable[idx] = e; |
| else |
| { |
| // Reuse trailing consecutive sequence at same slot |
| HashEntry lastRun = e; |
| int lastIdx = idx; |
| for (HashEntry last = next; last; last = last.next) |
| { |
| uint k = last.hash & sizeMask; |
| if (k != lastIdx) |
| { |
| lastIdx = k; |
| lastRun = last; |
| } |
| } |
| newTable[lastIdx] = lastRun; |
| |
| // Clone all remaining nodes |
| for (HashEntry p = e; p !is lastRun; p = p.next) |
| { |
| uint k = p.hash & sizeMask; |
| HashEntry n = newTable[k]; |
| newTable[k] = new HashEntry(p.key, p.hash, n, p.value); |
| } |
| } |
| } |
| } |
| volatile table = newTable; |
| } |
| |
| /** |
| * Remove; match on key only if value null, else match both. |
| */ |
| final synchronized V remove(K key, uint hash, V value) |
| { |
| int c; |
| |
| HashEntry[] tab; |
| |
| volatile c = count - 1; |
| volatile tab = table; |
| |
| uint index = hash & (tab.length - 1); |
| HashEntry first = tab[index]; |
| HashEntry e = first; |
| |
| while (e && (e.hash != hash || !matchKey(key, e.key))) |
| e = e.next; |
| |
| V oldValue = null; |
| if (e) |
| { |
| V v; |
| volatile v = e.value; |
| if (value is null || value == v) |
| { |
| oldValue = v; |
| |
| // All entries following removed node can stay |
| // in list, but all preceding ones need to be |
| // cloned. |
| HashEntry newFirst = e.next; |
| for (HashEntry p = first; p !is e; p = p.next) |
| newFirst = new HashEntry(p.key, p.hash, newFirst, p.value); |
| tab[index] = newFirst; |
| |
| // write-volatile |
| volatile count = c; |
| } |
| } |
| return oldValue; |
| } |
| |
| |
| final synchronized void clear() |
| { |
| if (count) |
| { |
| HashEntry[] tab; |
| volatile tab = table; |
| |
| for (int i = 0; i < tab.length; i++) |
| tab[i] = null; |
| |
| // write-volatile |
| volatile count = 0; |
| } |
| } |
| } |
| |
| |
| |
| /* ---------------- Public operations -------------- */ |
| |
| /** |
| * Creates a new, empty map with the specified initial |
| * capacity and the specified load factor. |
| * |
| * @param initialCapacity the initial capacity. The implementation |
| * performs internal sizing to accommodate this many elements. |
| * @param loadFactor the load factor threshold, used to control resizing. |
| * @param concurrencyLevel the estimated number of concurrently |
| * updating threads. The implementation performs internal sizing |
| * to try to accommodate this many threads. |
| * @throws IllegalArgumentException if the initial capacity is |
| * negative or the load factor or concurrencyLevel are |
| * nonpositive. |
| */ |
| public this(uint initialCapacity, float loadFactor, uint concurrencyLevel) |
| { |
| assert(loadFactor > 0); |
| |
| if (concurrencyLevel > MAX_SEGMENTS) |
| concurrencyLevel = MAX_SEGMENTS; |
| |
| // Find power-of-two sizes best matching arguments |
| int sshift = 0; |
| int ssize = 1; |
| while (ssize < concurrencyLevel) |
| { |
| ++sshift; |
| ssize <<= 1; |
| } |
| |
| segmentShift = 32 - sshift; |
| segmentMask = ssize - 1; |
| this.segments = new Segment[ssize]; |
| |
| if (initialCapacity > MAXIMUM_CAPACITY) |
| initialCapacity = MAXIMUM_CAPACITY; |
| |
| int c = initialCapacity / ssize; |
| if (c * ssize < initialCapacity) |
| ++c; |
| |
| int cap = 1; |
| while (cap < c) |
| cap <<= 1; |
| |
| for (int i = 0; i < this.segments.length; ++i) |
| this.segments[i] = new Segment(cap, loadFactor); |
| } |
| |
| /** |
| * Creates a new, empty map with the specified initial |
| * capacity, and with default load factor and concurrencyLevel. |
| * |
| * @param initialCapacity The implementation performs internal |
| * sizing to accommodate this many elements. |
| * @throws IllegalArgumentException if the initial capacity of |
| * elements is negative. |
| */ |
| public this(uint initialCapacity) |
| { |
| this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); |
| } |
| |
| /** |
| * Creates a new, empty map with a default initial capacity, |
| * load factor, and concurrencyLevel. |
| */ |
| public this() |
| { |
| this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); |
| } |
| |
| /** |
| * Returns the value to which the specified key is mapped in this table. |
| * |
| * @param key a key in the table. |
| * @return the value to which the key is mapped in this table; |
| * <tt>null</tt> if the key is not mapped to any value in |
| * this table. |
| * @throws NullPointerException if the key is |
| * <tt>null</tt>. |
| */ |
| public V get(K key) |
| { |
| uint hash = hash(key); // throws NullPointerException if key null |
| |
| return segmentFor(hash).get(key, hash); |
| } |
| |
| /** |
| * Tests if the specified object is a key in this table. |
| * |
| * @param key possible key. |
| * @return <tt>true</tt> if and only if the specified object |
| * is a key in this table, as determined by the |
| * <tt>equals</tt> method; <tt>false</tt> otherwise. |
| * @throws NullPointerException if the key is |
| * <tt>null</tt>. |
| */ |
| public bool containsKey(K key) |
| { |
| uint hash = hash(key); // throws NullPointerException if key null |
| |
| return segmentFor(hash).containsKey(key, hash); |
| } |
| |
| /** |
| * Maps the specified <tt>key</tt> to the specified |
| * <tt>value</tt> in this table. Neither the key nor the |
| * value can be <tt>null</tt>. |
| * |
| * <p> The value can be retrieved by calling the <tt>get</tt> method |
| * with a key that is equal to the original key. |
| * |
| * @param key the table key. |
| * @param value the value. |
| * @return the previous value of the specified key in this table, |
| * or <tt>null</tt> if it did not have one. |
| * @throws NullPointerException if the key or value is |
| * <tt>null</tt>. |
| */ |
| public V put(K key, V value) |
| { |
| assert(value); |
| |
| uint hash = hash(key); |
| return segmentFor(hash).put(key, hash, value, false); |
| } |
| |
| /** |
| * If the specified key is not already associated |
| * with a value, associate it with the given value. |
| * This is equivalent to |
| * <pre> |
| * if (!map.containsKey(key)) |
| * return map.put(key, value); |
| * else |
| * return map.get(key); |
| * </pre> |
| * Except that the action is performed atomically. |
| * @param key key with which the specified value is to be associated. |
| * @param value value to be associated with the specified key. |
| * @return previous value associated with specified key, or <tt>null</tt> |
| * if there was no mapping for key. |
| * @throws NullPointerException if the specified key or value is |
| * <tt>null</tt>. |
| */ |
| public V putIfAbsent(K key, V value) |
| { |
| assert(value); |
| |
| uint hash = hash(key); |
| return segmentFor(hash).put(key, hash, value, true); |
| } |
| |
| |
| /** |
| * Removes the key (and its corresponding value) from this |
| * table. This method does nothing if the key is not in the table. |
| * |
| * @param key the key that needs to be removed. |
| * @return the value to which the key had been mapped in this table, |
| * or <tt>null</tt> if the key did not have a mapping. |
| * @throws NullPointerException if the key is |
| * <tt>null</tt>. |
| */ |
| public V remove(K key) |
| { |
| uint hash = hash(key); |
| |
| return segmentFor(hash).remove(key, hash, null); |
| } |
| |
| /** |
| * Remove entry for key only if currently mapped to given value. |
| * Acts as |
| * <pre> |
| * if (map.get(key).equals(value)) { |
| * map.remove(key); |
| * return true; |
| * } else return false; |
| * </pre> |
| * except that the action is performed atomically. |
| * @param key key with which the specified value is associated. |
| * @param value value associated with the specified key. |
| * @return true if the value was removed |
| * @throws NullPointerException if the specified key is |
| * <tt>null</tt>. |
| */ |
| public bool remove(K key, V value) |
| { |
| uint hash = hash(key); |
| |
| return cast(bool)(segmentFor(hash).remove(key, hash, value) !is null); |
| } |
| |
| |
| /** |
| * Replace entry for key only if currently mapped to some value. |
| * Acts as |
| * <pre> |
| * if ((map.containsKey(key)) { |
| * return map.put(key, value); |
| * } else return null; |
| * </pre> |
| * except that the action is performed atomically. |
| * @param key key with which the specified value is associated. |
| * @param value value to be associated with the specified key. |
| * @return previous value associated with specified key, or <tt>null</tt> |
| * if there was no mapping for key. |
| * @throws NullPointerException if the specified key or value is |
| * <tt>null</tt>. |
| */ |
| public V replace(K key, V value) |
| { |
| assert(value); |
| |
| uint hash = hash(key); |
| return segmentFor(hash).replace(key, hash, value); |
| } |
| |
| |
| /** |
| * Removes all mappings from this map. |
| */ |
| public void clear() |
| { |
| for (int i = 0; i < segments.length; ++i) |
| segments[i].clear(); |
| } |
| |
| |
| /** |
| * Returns an enumeration of the keys in this table. |
| * |
| * @return an enumeration of the keys in this table. |
| * @see #keySet |
| */ |
| public KeyIterator keys() |
| { |
| return new KeyIterator(this); |
| } |
| |
| /** |
| * Returns an enumeration of the values in this table. |
| * |
| * @return an enumeration of the values in this table. |
| * @see #values |
| */ |
| public ValueIterator elements() |
| { |
| return new ValueIterator(this); |
| } |
| |
| /********************************************************************** |
| |
| Iterate over all keys in hashmap |
| |
| **********************************************************************/ |
| |
| int opApply(int delegate(inout char[]) dg) |
| { |
| int result = 0; |
| KeyIterator iterator = keys(); |
| |
| while (iterator.hasNext) |
| { |
| char[] ca = cast(char[])iterator.next; |
| if ((result = dg(ca)) != 0) |
| break; |
| } |
| return result; |
| } |
| |
| /********************************************************************** |
| |
| Iterate over all keys in hashmap |
| |
| **********************************************************************/ |
| |
| int opApply(int delegate(inout char[], inout Object) dg) |
| { |
| int result = 0; |
| KeyIterator iterator = keys(); |
| |
| while (iterator.hasNext) |
| { |
| HashEntry he = iterator.nextElement; |
| char[] ca = cast(char[])he.key; |
| if ((result = dg(ca, he.value)) != 0) |
| break; |
| } |
| return result; |
| } |
| |
| |
| /* ---------------- Iterator Support -------------- */ |
| |
| abstract static class HashIterator |
| { |
| int nextSegmentIndex; |
| int nextTableIndex; |
| HashEntry[] currentTable; |
| HashEntry nextEntry; |
| HashEntry lastReturned; |
| HashMap map; |
| |
| this(HashMap map) |
| { |
| this.map = map; |
| nextSegmentIndex = map.segments.length - 1; |
| nextTableIndex = -1; |
| advance(); |
| } |
| |
| final void advance() |
| { |
| if (nextEntry !is null && (nextEntry = nextEntry.next) !is null) |
| return; |
| |
| while (nextTableIndex >= 0) |
| { |
| if ((nextEntry = currentTable[nextTableIndex--]) !is null) |
| return; |
| } |
| |
| while (nextSegmentIndex >= 0) |
| { |
| Segment seg = map.segments[nextSegmentIndex--]; |
| volatile if (seg.count) |
| { |
| currentTable = seg.table; |
| for (int j = currentTable.length - 1; j >= 0; --j) |
| { |
| if ((nextEntry = currentTable[j]) !is null) |
| { |
| nextTableIndex = j - 1; |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| public bool hasNext() |
| { |
| return cast(bool)(nextEntry !is null); |
| } |
| |
| HashEntry nextElement() |
| { |
| if (nextEntry is null) |
| throw new Exception("no such element in HashMap"); |
| |
| lastReturned = nextEntry; |
| advance(); |
| return lastReturned; |
| } |
| } |
| |
| static class KeyIterator : HashIterator |
| { |
| this(HashMap map) { |
| super(map); |
| } |
| public K next() |
| { |
| return super.nextElement().key; |
| } |
| } |
| |
| static class ValueIterator : HashIterator |
| { |
| this(HashMap map) { |
| super(map); |
| } |
| public V next() |
| { |
| volatile return super.nextElement().value; |
| } |
| } |
| } |