PageRenderTime 64ms CodeModel.GetById 36ms app.highlight 22ms RepoModel.GetById 0ms app.codeStats 0ms

/jboss-as-7.1.1.Final/controller/src/main/java/org/jboss/as/controller/registry/FastCopyHashMap.java

#
Java | 759 lines | 524 code | 155 blank | 80 comment | 130 complexity | ae917ae03c3d2b28ac57d2a82b476def MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  1/*
  2 * JBoss, Home of Professional Open Source.
  3 * Copyright 2011, Red Hat, Inc., and individual contributors
  4 * as indicated by the @author tags. See the copyright.txt file in the
  5 * distribution for a full listing of individual contributors.
  6 *
  7 * This is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU Lesser General Public License as
  9 * published by the Free Software Foundation; either version 2.1 of
 10 * the License, or (at your option) any later version.
 11 *
 12 * This software is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Lesser General Public
 18 * License along with this software; if not, write to the Free
 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21 */
 22
 23package org.jboss.as.controller.registry;
 24
 25import java.io.IOException;
 26import java.io.Serializable;
 27import java.util.AbstractCollection;
 28import java.util.AbstractMap;
 29import java.util.AbstractSet;
 30import java.util.Collection;
 31import java.util.ConcurrentModificationException;
 32import java.util.Iterator;
 33import java.util.Map;
 34import java.util.NoSuchElementException;
 35import java.util.Set;
 36
 37import static org.jboss.as.controller.ControllerMessages.MESSAGES;
 38
 39/**
 40 * A HashMap that is optimized for fast shallow copies. If the copy-ctor is
 41 * passed another FastCopyHashMap, or clone is called on this map, the shallow
 42 * copy can be performed using little more than a single array copy. In order to
 43 * accomplish this, immutable objects must be used internally, so update
 44 * operations result in slightly more object churn than <code>HashMap</code>.
 45 * <p/>
 46 * Note: It is very important to use a smaller load factor than you normally
 47 * would for HashMap, since the implementation is open-addressed with linear
 48 * probing. With a 50% load-factor a get is expected to return in only 2 probes.
 49 * However, a 90% load-factor is expected to return in around 50 probes.
 50 *
 51 * @author Jason T. Greene
 52 */
 53class FastCopyHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
 54    /**
 55     * Marks null keys.
 56     */
 57    private static final Object NULL = new Object();
 58
 59    /**
 60     * Serialization ID
 61     */
 62    private static final long serialVersionUID = 10929568968762L;
 63
 64    /**
 65     * Same default as HashMap, must be a power of 2
 66     */
 67    private static final int DEFAULT_CAPACITY = 8;
 68
 69    /**
 70     * MAX_INT - 1
 71     */
 72    private static final int MAXIMUM_CAPACITY = 1 << 30;
 73
 74    /**
 75     * 67%, just like IdentityHashMap
 76     */
 77    private static final float DEFAULT_LOAD_FACTOR = 0.67f;
 78
 79    /**
 80     * The open-addressed table
 81     */
 82    private transient Entry<K, V>[] table;
 83
 84    /**
 85     * The current number of key-value pairs
 86     */
 87    private transient int size;
 88
 89    /**
 90     * The next resize
 91     */
 92    private transient int threshold;
 93
 94    /**
 95     * The user defined load factor which defines when to resize
 96     */
 97    private final float loadFactor;
 98
 99    /**
100     * Counter used to detect changes made outside of an iterator
101     */
102    private transient int modCount;
103
104    // Cached views
105    private transient KeySet keySet;
106    private transient Values values;
107    private transient EntrySet entrySet;
108
109    public FastCopyHashMap(int initialCapacity, float loadFactor) {
110        if (initialCapacity < 0)
111            throw MESSAGES.invalidTableSize();
112
113        if (initialCapacity > MAXIMUM_CAPACITY)
114            initialCapacity = MAXIMUM_CAPACITY;
115
116        if (!(loadFactor > 0F && loadFactor <= 1F))
117            throw MESSAGES.invalidLoadFactor();
118
119        this.loadFactor = loadFactor;
120        init(initialCapacity, loadFactor);
121    }
122
123    @SuppressWarnings("unchecked")
124    public FastCopyHashMap(Map<? extends K, ? extends V> map) {
125        if (map instanceof FastCopyHashMap) {
126            FastCopyHashMap<? extends K, ? extends V> fast = (FastCopyHashMap<? extends K, ? extends V>) map;
127            this.table = (Entry<K, V>[]) fast.table.clone();
128            this.loadFactor = fast.loadFactor;
129            this.size = fast.size;
130            this.threshold = fast.threshold;
131        } else {
132            this.loadFactor = DEFAULT_LOAD_FACTOR;
133            init(map.size(), this.loadFactor);
134            putAll(map);
135        }
136    }
137
138    @SuppressWarnings("unchecked")
139    private void init(int initialCapacity, float loadFactor) {
140        int c = 1;
141        while (c < initialCapacity) c <<= 1;
142        threshold = (int) (c * loadFactor);
143
144        // Include the load factor when sizing the table for the first time
145        if (initialCapacity > threshold && c < MAXIMUM_CAPACITY) {
146            c <<= 1;
147            threshold = (int) (c * loadFactor);
148        }
149
150        this.table = (Entry<K, V>[]) new Entry[c];
151    }
152
153    public FastCopyHashMap(int initialCapacity) {
154        this(initialCapacity, DEFAULT_LOAD_FACTOR);
155    }
156
157    public FastCopyHashMap() {
158        this(DEFAULT_CAPACITY);
159    }
160
161    // The normal bit spreader...
162
163    private static int hash(Object key) {
164        int h = key.hashCode();
165        h ^= (h >>> 20) ^ (h >>> 12);
166        return h ^ (h >>> 7) ^ (h >>> 4);
167    }
168
169    @SuppressWarnings("unchecked")
170    private static <K> K maskNull(K key) {
171        return key == null ? (K) NULL : key;
172    }
173
174    private static <K> K unmaskNull(K key) {
175        return key == NULL ? null : key;
176    }
177
178    private int nextIndex(int index, int length) {
179        index = (index >= length - 1) ? 0 : index + 1;
180        return index;
181    }
182
183    private static boolean eq(Object o1, Object o2) {
184        return o1 == o2 || (o1 != null && o1.equals(o2));
185    }
186
187    private static int index(int hashCode, int length) {
188        return hashCode & (length - 1);
189    }
190
191    public int size() {
192        return size;
193    }
194
195    public boolean isEmpty() {
196        return size == 0;
197    }
198
199    public V get(Object key) {
200        key = maskNull(key);
201
202        int hash = hash(key);
203        int length = table.length;
204        int index = index(hash, length);
205
206        for (int start = index; ;) {
207            Entry<K, V> e = table[index];
208            if (e == null)
209                return null;
210
211            if (e.hash == hash && eq(key, e.key))
212                return e.value;
213
214            index = nextIndex(index, length);
215            if (index == start) // Full table
216                return null;
217        }
218    }
219
220    public boolean containsKey(Object key) {
221        key = maskNull(key);
222
223        int hash = hash(key);
224        int length = table.length;
225        int index = index(hash, length);
226
227        for (int start = index; ;) {
228            Entry<K, V> e = table[index];
229            if (e == null)
230                return false;
231
232            if (e.hash == hash && eq(key, e.key))
233                return true;
234
235            index = nextIndex(index, length);
236            if (index == start) // Full table
237                return false;
238        }
239    }
240
241    public boolean containsValue(Object value) {
242        for (Entry<K, V> e : table)
243            if (e != null && eq(value, e.value))
244                return true;
245
246        return false;
247    }
248
249    public V put(K key, V value) {
250        key = maskNull(key);
251
252        Entry<K, V>[] table = this.table;
253        int hash = hash(key);
254        int length = table.length;
255        int index = index(hash, length);
256
257        for (int start = index; ;) {
258            Entry<K, V> e = table[index];
259            if (e == null)
260                break;
261
262            if (e.hash == hash && eq(key, e.key)) {
263                table[index] = new Entry<K, V>(e.key, e.hash, value);
264                return e.value;
265            }
266
267            index = nextIndex(index, length);
268            if (index == start)
269                throw MESSAGES.tableIsFull();
270        }
271
272        modCount++;
273        table[index] = new Entry<K, V>(key, hash, value);
274        if (++size >= threshold)
275            resize(length);
276
277        return null;
278    }
279
280
281    @SuppressWarnings("unchecked")
282    private void resize(int from) {
283        int newLength = from << 1;
284
285        // Can't get any bigger
286        if (newLength > MAXIMUM_CAPACITY || newLength <= from)
287            return;
288
289        Entry<K, V>[] newTable = new Entry[newLength];
290        Entry<K, V>[] old = table;
291
292        for (Entry<K, V> e : old) {
293            if (e == null)
294                continue;
295
296            int index = index(e.hash, newLength);
297            while (newTable[index] != null)
298                index = nextIndex(index, newLength);
299
300            newTable[index] = e;
301        }
302
303        threshold = (int) (loadFactor * newLength);
304        table = newTable;
305    }
306
307    public void putAll(Map<? extends K, ? extends V> map) {
308        int size = map.size();
309        if (size == 0)
310            return;
311
312        if (size > threshold) {
313            if (size > MAXIMUM_CAPACITY)
314                size = MAXIMUM_CAPACITY;
315
316            int length = table.length;
317            while (length < size) length <<= 1;
318
319            resize(length);
320        }
321
322        for (Map.Entry<? extends K, ? extends V> e : map.entrySet())
323            put(e.getKey(), e.getValue());
324    }
325
326    public V remove(Object key) {
327        key = maskNull(key);
328
329        Entry<K, V>[] table = this.table;
330        int length = table.length;
331        int hash = hash(key);
332        int start = index(hash, length);
333
334        for (int index = start; ;) {
335            Entry<K, V> e = table[index];
336            if (e == null)
337                return null;
338
339            if (e.hash == hash && eq(key, e.key)) {
340                table[index] = null;
341                relocate(index);
342                modCount++;
343                size--;
344                return e.value;
345            }
346
347            index = nextIndex(index, length);
348            if (index == start)
349                return null;
350        }
351
352
353    }
354
355    private void relocate(int start) {
356        Entry<K, V>[] table = this.table;
357        int length = table.length;
358        int current = nextIndex(start, length);
359
360        for (; ;) {
361            Entry<K, V> e = table[current];
362            if (e == null)
363                return;
364
365            // A Doug Lea variant of Knuth's Section 6.4 Algorithm R.
366            // This provides a non-recursive method of relocating
367            // entries to their optimal positions once a gap is created.
368            int prefer = index(e.hash, length);
369            if ((current < prefer && (prefer <= start || start <= current))
370                    || (prefer <= start && start <= current)) {
371                table[start] = e;
372                table[current] = null;
373                start = current;
374            }
375
376            current = nextIndex(current, length);
377        }
378    }
379
380    public void clear() {
381        modCount++;
382        Entry<K, V>[] table = this.table;
383        for (int i = 0; i < table.length; i++)
384            table[i] = null;
385
386        size = 0;
387    }
388
389    @SuppressWarnings("unchecked")
390    public FastCopyHashMap<K, V> clone() {
391        try {
392            FastCopyHashMap<K, V> clone = (FastCopyHashMap<K, V>) super.clone();
393            clone.table = table.clone();
394            clone.entrySet = null;
395            clone.values = null;
396            clone.keySet = null;
397            return clone;
398        }
399        catch (CloneNotSupportedException e) {
400            // should never happen
401            throw new IllegalStateException(e);
402        }
403    }
404
405    public void printDebugStats() {
406        int optimal = 0;
407        int total = 0;
408        int totalSkew = 0;
409        int maxSkew = 0;
410        for (int i = 0; i < table.length; i++) {
411            Entry<K, V> e = table[i];
412            if (e != null) {
413
414                total++;
415                int target = index(e.hash, table.length);
416                if (i == target)
417                    optimal++;
418                else {
419                    int skew = Math.abs(i - target);
420                    if (skew > maxSkew) maxSkew = skew;
421                    totalSkew += skew;
422                }
423
424            }
425        }
426
427        System.out.println(" Size:            " + size);
428        System.out.println(" Real Size:       " + total);
429        System.out.println(" Optimal:         " + optimal + " (" + (float) optimal * 100 / total + "%)");
430        System.out.println(" Average Distnce: " + ((float) totalSkew / (total - optimal)));
431        System.out.println(" Max Distance:    " + maxSkew);
432    }
433
434    public Set<Map.Entry<K, V>> entrySet() {
435        if (entrySet == null)
436            entrySet = new EntrySet();
437
438        return entrySet;
439    }
440
441    public Set<K> keySet() {
442        if (keySet == null)
443            keySet = new KeySet();
444
445        return keySet;
446    }
447
448    public Collection<V> values() {
449        if (values == null)
450            values = new Values();
451
452        return values;
453    }
454
455    @SuppressWarnings("unchecked")
456    private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
457        s.defaultReadObject();
458
459        int size = s.readInt();
460
461        init(size, loadFactor);
462
463        for (int i = 0; i < size; i++) {
464            K key = (K) s.readObject();
465            V value = (V) s.readObject();
466            putForCreate(key, value);
467        }
468
469        this.size = size;
470    }
471
472    @SuppressWarnings("unchecked")
473    private void putForCreate(K key, V value) {
474        key = maskNull(key);
475
476        Entry<K, V>[] table = this.table;
477        int hash = hash(key);
478        int length = table.length;
479        int index = index(hash, length);
480
481        Entry<K, V> e = table[index];
482        while (e != null) {
483            index = nextIndex(index, length);
484            e = table[index];
485        }
486
487        table[index] = new Entry<K, V>(key, hash, value);
488    }
489
490    private void writeObject(java.io.ObjectOutputStream s) throws IOException {
491        s.defaultWriteObject();
492        s.writeInt(size);
493
494        for (Entry<K, V> e : table) {
495            if (e != null) {
496                s.writeObject(unmaskNull(e.key));
497                s.writeObject(e.value);
498            }
499        }
500    }
501
502    private static final class Entry<K, V> {
503        final K key;
504        final int hash;
505        final V value;
506
507        Entry(K key, int hash, V value) {
508            this.key = key;
509            this.hash = hash;
510            this.value = value;
511        }
512    }
513
514    private abstract class FasyCopyHashMapIterator<E> implements Iterator<E> {
515        private int next = 0;
516        private int expectedCount = modCount;
517        private int current = -1;
518        private boolean hasNext;
519        Entry<K, V>[] table = FastCopyHashMap.this.table;
520
521        public boolean hasNext() {
522            if (hasNext == true)
523                return true;
524
525            Entry<K, V>[] table = this.table;
526            for (int i = next; i < table.length; i++) {
527                if (table[i] != null) {
528                    next = i;
529                    return hasNext = true;
530                }
531            }
532
533            next = table.length;
534            return false;
535        }
536
537        protected Entry<K, V> nextEntry() {
538            if (modCount != expectedCount)
539                throw new ConcurrentModificationException();
540
541            if (!hasNext && !hasNext())
542                throw new NoSuchElementException();
543
544            current = next++;
545            hasNext = false;
546
547            return table[current];
548        }
549
550        @SuppressWarnings("unchecked")
551        public void remove() {
552            if (modCount != expectedCount)
553                throw new ConcurrentModificationException();
554
555            int current = this.current;
556            int delete = current;
557
558            if (current == -1)
559                throw new IllegalStateException();
560
561            // Invalidate current (prevents multiple remove)
562            this.current = -1;
563
564            // Start were we relocate
565            next = delete;
566
567            Entry<K, V>[] table = this.table;
568            if (table != FastCopyHashMap.this.table) {
569                FastCopyHashMap.this.remove(table[delete].key);
570                table[delete] = null;
571                expectedCount = modCount;
572                return;
573            }
574
575
576            int length = table.length;
577            int i = delete;
578
579            table[delete] = null;
580            size--;
581
582            for (; ;) {
583                i = nextIndex(i, length);
584                Entry<K, V> e = table[i];
585                if (e == null)
586                    break;
587
588                int prefer = index(e.hash, length);
589                if ((i < prefer && (prefer <= delete || delete <= i))
590                        || (prefer <= delete && delete <= i)) {
591                    // Snapshot the unseen portion of the table if we have
592                    // to relocate an entry that was already seen by this iterator
593                    if (i < current && current <= delete && table == FastCopyHashMap.this.table) {
594                        int remaining = length - current;
595                        Entry<K, V>[] newTable = (Entry<K, V>[]) new Entry[remaining];
596                        System.arraycopy(table, current, newTable, 0, remaining);
597
598                        // Replace iterator's table.
599                        // Leave table local var pointing to the real table
600                        this.table = newTable;
601                        next = 0;
602                    }
603
604                    // Do the swap on the real table
605                    table[delete] = e;
606                    table[i] = null;
607                    delete = i;
608                }
609            }
610        }
611    }
612
613
614    private class KeyIterator extends FasyCopyHashMapIterator<K> {
615        public K next() {
616            return unmaskNull(nextEntry().key);
617        }
618    }
619
620    private class ValueIterator extends FasyCopyHashMapIterator<V> {
621        public V next() {
622            return nextEntry().value;
623        }
624    }
625
626    private class EntryIterator extends FasyCopyHashMapIterator<Map.Entry<K, V>> {
627        private class WriteThroughEntry extends SimpleEntry<K, V> {
628            WriteThroughEntry(K key, V value) {
629                super(key, value);
630            }
631
632            public V setValue(V value) {
633                if (table != FastCopyHashMap.this.table)
634                    FastCopyHashMap.this.put(getKey(), value);
635
636                return super.setValue(value);
637            }
638        }
639
640        public Map.Entry<K, V> next() {
641            Entry<K, V> e = nextEntry();
642            return new WriteThroughEntry(unmaskNull(e.key), e.value);
643        }
644
645    }
646
647    private class KeySet extends AbstractSet<K> {
648        public Iterator<K> iterator() {
649            return new KeyIterator();
650        }
651
652        public void clear() {
653            FastCopyHashMap.this.clear();
654        }
655
656        public boolean contains(Object o) {
657            return containsKey(o);
658        }
659
660        public boolean remove(Object o) {
661            int size = size();
662            FastCopyHashMap.this.remove(o);
663            return size() < size;
664        }
665
666        public int size() {
667            return FastCopyHashMap.this.size();
668        }
669    }
670
671    private class Values extends AbstractCollection<V> {
672        public Iterator<V> iterator() {
673            return new ValueIterator();
674        }
675
676        public void clear() {
677            FastCopyHashMap.this.clear();
678        }
679
680        public int size() {
681            return FastCopyHashMap.this.size();
682        }
683    }
684
685    private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
686        public Iterator<Map.Entry<K, V>> iterator() {
687            return new EntryIterator();
688        }
689
690        public boolean contains(Object o) {
691            if (!(o instanceof Map.Entry))
692                return false;
693
694            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
695            Object value = get(entry.getKey());
696            return eq(entry.getValue(), value);
697        }
698
699        public void clear() {
700            FastCopyHashMap.this.clear();
701        }
702
703        public boolean isEmpty() {
704            return FastCopyHashMap.this.isEmpty();
705        }
706
707        public int size() {
708            return FastCopyHashMap.this.size();
709        }
710    }
711
712    protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
713        private K key;
714        private V value;
715
716        SimpleEntry(K key, V value) {
717            this.key = key;
718            this.value = value;
719        }
720
721        SimpleEntry(Map.Entry<K, V> entry) {
722            this.key = entry.getKey();
723            this.value = entry.getValue();
724        }
725
726        public K getKey() {
727            return key;
728        }
729
730        public V getValue() {
731            return value;
732        }
733
734        public V setValue(V value) {
735            V old = this.value;
736            this.value = value;
737            return old;
738        }
739
740        public boolean equals(Object o) {
741            if (this == o)
742                return true;
743
744            if (!(o instanceof Map.Entry))
745                return false;
746            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
747            return eq(key, e.getKey()) && eq(value, e.getValue());
748        }
749
750        public int hashCode() {
751            return (key == null ? 0 : hash(key)) ^
752                    (value == null ? 0 : hash(value));
753        }
754
755        public String toString() {
756            return getKey() + "=" + getValue();
757        }
758    }
759}