ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java (file contents):
Revision 1.248 by jsr166, Sun Sep 1 05:04:18 2013 UTC vs.
Revision 1.278 by jsr166, Sat Sep 12 21:55:08 2015 UTC

# Line 13 | Line 13 | import java.lang.reflect.Type;
13   import java.util.AbstractMap;
14   import java.util.Arrays;
15   import java.util.Collection;
16 import java.util.Comparator;
16   import java.util.Enumeration;
17   import java.util.HashMap;
18   import java.util.Hashtable;
# Line 22 | Line 21 | import java.util.Map;
21   import java.util.NoSuchElementException;
22   import java.util.Set;
23   import java.util.Spliterator;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.ForkJoinPool;
24   import java.util.concurrent.atomic.AtomicReference;
25   import java.util.concurrent.locks.LockSupport;
26   import java.util.concurrent.locks.ReentrantLock;
27   import java.util.function.BiConsumer;
28   import java.util.function.BiFunction;
32 import java.util.function.BinaryOperator;
29   import java.util.function.Consumer;
30   import java.util.function.DoubleBinaryOperator;
31   import java.util.function.Function;
32   import java.util.function.IntBinaryOperator;
33   import java.util.function.LongBinaryOperator;
34 + import java.util.function.Predicate;
35   import java.util.function.ToDoubleBiFunction;
36   import java.util.function.ToDoubleFunction;
37   import java.util.function.ToIntBiFunction;
# Line 104 | Line 101 | import java.util.stream.Stream;
101   * mapped values are (perhaps transiently) not used or all take the
102   * same mapping value.
103   *
104 < * <p>A ConcurrentHashMap can be used as scalable frequency map (a
104 > * <p>A ConcurrentHashMap can be used as a scalable frequency map (a
105   * form of histogram or multiset) by using {@link
106   * java.util.concurrent.atomic.LongAdder} values and initializing via
107   * {@link #computeIfAbsent computeIfAbsent}. For example, to add a count
108   * to a {@code ConcurrentHashMap<String,LongAdder> freqs}, you can use
109 < * {@code freqs.computeIfAbsent(k -> new LongAdder()).increment();}
109 > * {@code freqs.computeIfAbsent(key, k -> new LongAdder()).increment();}
110   *
111   * <p>This class and its views and iterators implement all of the
112   * <em>optional</em> methods of the {@link Map} and {@link Iterator}
# Line 124 | Line 121 | import java.util.stream.Stream;
121   * being concurrently updated by other threads; for example, when
122   * computing a snapshot summary of the values in a shared registry.
123   * There are three kinds of operation, each with four forms, accepting
124 < * functions with Keys, Values, Entries, and (Key, Value) arguments
125 < * and/or return values. Because the elements of a ConcurrentHashMap
126 < * are not ordered in any particular way, and may be processed in
127 < * different orders in different parallel executions, the correctness
128 < * of supplied functions should not depend on any ordering, or on any
129 < * other objects or values that may transiently change while
130 < * computation is in progress; and except for forEach actions, should
131 < * ideally be side-effect-free. Bulk operations on {@link java.util.Map.Entry}
132 < * objects do not support method {@code setValue}.
124 > * functions with keys, values, entries, and (key, value) pairs as
125 > * arguments and/or return values. Because the elements of a
126 > * ConcurrentHashMap are not ordered in any particular way, and may be
127 > * processed in different orders in different parallel executions, the
128 > * correctness of supplied functions should not depend on any
129 > * ordering, or on any other objects or values that may transiently
130 > * change while computation is in progress; and except for forEach
131 > * actions, should ideally be side-effect-free. Bulk operations on
132 > * {@link java.util.Map.Entry} objects do not support method {@code
133 > * setValue}.
134   *
135   * <ul>
136   * <li> forEach: Perform a given action on each element.
# Line 351 | Line 349 | public class ConcurrentHashMap<K,V> exte
349       * progress.  Resizing proceeds by transferring bins, one by one,
350       * from the table to the next table. However, threads claim small
351       * blocks of indices to transfer (via field transferIndex) before
352 <     * doing so, reducing contention.  Because we are using
353 <     * power-of-two expansion, the elements from each bin must either
354 <     * stay at same index, or move with a power of two offset. We
355 <     * eliminate unnecessary node creation by catching cases where old
356 <     * nodes can be reused because their next fields won't change.  On
357 <     * average, only about one-sixth of them need cloning when a table
358 <     * doubles. The nodes they replace will be garbage collectable as
359 <     * soon as they are no longer referenced by any reader thread that
360 <     * may be in the midst of concurrently traversing table.  Upon
361 <     * transfer, the old table bin contains only a special forwarding
362 <     * node (with hash field "MOVED") that contains the next table as
363 <     * its key. On encountering a forwarding node, access and update
364 <     * operations restart, using the new table.
352 >     * doing so, reducing contention.  A generation stamp in field
353 >     * sizeCtl ensures that resizings do not overlap. Because we are
354 >     * using power-of-two expansion, the elements from each bin must
355 >     * either stay at same index, or move with a power of two
356 >     * offset. We eliminate unnecessary node creation by catching
357 >     * cases where old nodes can be reused because their next fields
358 >     * won't change.  On average, only about one-sixth of them need
359 >     * cloning when a table doubles. The nodes they replace will be
360 >     * garbage collectable as soon as they are no longer referenced by
361 >     * any reader thread that may be in the midst of concurrently
362 >     * traversing table.  Upon transfer, the old table bin contains
363 >     * only a special forwarding node (with hash field "MOVED") that
364 >     * contains the next table as its key. On encountering a
365 >     * forwarding node, access and update operations restart, using
366 >     * the new table.
367       *
368       * Each bin transfer requires its bin lock, which can stall
369       * waiting for locks while resizing. However, because other
# Line 449 | Line 449 | public class ConcurrentHashMap<K,V> exte
449       *
450       * Maintaining API and serialization compatibility with previous
451       * versions of this class introduces several oddities. Mainly: We
452 <     * leave untouched but unused constructor arguments refering to
452 >     * leave untouched but unused constructor arguments referring to
453       * concurrencyLevel. We accept a loadFactor constructor argument,
454       * but apply it only to initial table capacity (which is the only
455       * time that we can guarantee to honor it.) We also declare an
# Line 540 | Line 540 | public class ConcurrentHashMap<K,V> exte
540       */
541      private static final int MIN_TRANSFER_STRIDE = 16;
542  
543 +    /**
544 +     * The number of bits used for generation stamp in sizeCtl.
545 +     * Must be at least 6 for 32bit arrays.
546 +     */
547 +    private static int RESIZE_STAMP_BITS = 16;
548 +
549 +    /**
550 +     * The maximum number of threads that can help resize.
551 +     * Must fit in 32 - RESIZE_STAMP_BITS bits.
552 +     */
553 +    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
554 +
555 +    /**
556 +     * The bit shift for recording size stamp in sizeCtl.
557 +     */
558 +    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
559 +
560      /*
561       * Encodings for Node hash fields. See above for explanation.
562       */
# Line 581 | Line 598 | public class ConcurrentHashMap<K,V> exte
598              this.next = next;
599          }
600  
601 <        public final K getKey()       { return key; }
602 <        public final V getValue()     { return val; }
603 <        public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
604 <        public final String toString(){ return key + "=" + val; }
601 >        public final K getKey()     { return key; }
602 >        public final V getValue()   { return val; }
603 >        public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
604 >        public final String toString() {
605 >            return Helpers.mapEntryToString(key, val);
606 >        }
607          public final V setValue(V value) {
608              throw new UnsupportedOperationException();
609          }
# Line 697 | Line 716 | public class ConcurrentHashMap<K,V> exte
716       * errors by users, these checks must operate on local variables,
717       * which accounts for some odd-looking inline assignments below.
718       * Note that calls to setTabAt always occur within locked regions,
719 <     * and so in principle require only release ordering, not need
719 >     * and so in principle require only release ordering, not
720       * full volatile semantics, but are currently coded as volatile
721       * writes to be conservative.
722       */
# Line 1008 | Line 1027 | public class ConcurrentHashMap<K,V> exte
1027                                      p.val = value;
1028                              }
1029                          }
1030 +                        else if (f instanceof ReservationNode)
1031 +                            throw new IllegalStateException("Recursive update");
1032                      }
1033                  }
1034                  if (binCount != 0) {
# Line 1110 | Line 1131 | public class ConcurrentHashMap<K,V> exte
1131                                  }
1132                              }
1133                          }
1134 +                        else if (f instanceof ReservationNode)
1135 +                            throw new IllegalStateException("Recursive update");
1136                      }
1137                  }
1138                  if (validated) {
# Line 1352 | Line 1375 | public class ConcurrentHashMap<K,V> exte
1375              new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
1376          for (int i = 0; i < segments.length; ++i)
1377              segments[i] = new Segment<K,V>(LOAD_FACTOR);
1378 <        s.putFields().put("segments", segments);
1379 <        s.putFields().put("segmentShift", segmentShift);
1380 <        s.putFields().put("segmentMask", segmentMask);
1378 >        java.io.ObjectOutputStream.PutField streamFields = s.putFields();
1379 >        streamFields.put("segments", segments);
1380 >        streamFields.put("segmentShift", segmentShift);
1381 >        streamFields.put("segmentMask", segmentMask);
1382          s.writeFields();
1383  
1384          Node<K,V>[] t;
# Line 1571 | Line 1595 | public class ConcurrentHashMap<K,V> exte
1595      }
1596  
1597      /**
1598 +     * Helper method for EntrySetView.removeIf
1599 +     */
1600 +    boolean removeEntryIf(Predicate<? super Entry<K,V>> function) {
1601 +        if (function == null) throw new NullPointerException();
1602 +        Node<K,V>[] t;
1603 +        boolean removed = false;
1604 +        if ((t = table) != null) {
1605 +            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
1606 +            for (Node<K,V> p; (p = it.advance()) != null; ) {
1607 +                K k = p.key;
1608 +                V v = p.val;
1609 +                Map.Entry<K,V> e = new AbstractMap.SimpleImmutableEntry<>(k, v);
1610 +                if (function.test(e) && replaceNode(k, null, v) != null)
1611 +                    removed = true;
1612 +            }
1613 +        }
1614 +        return removed;
1615 +    }
1616 +
1617 +    /**
1618 +     * Helper method for ValuesView.removeIf
1619 +     */
1620 +    boolean removeValueIf(Predicate<? super V> function) {
1621 +        if (function == null) throw new NullPointerException();
1622 +        Node<K,V>[] t;
1623 +        boolean removed = false;
1624 +        if ((t = table) != null) {
1625 +            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
1626 +            for (Node<K,V> p; (p = it.advance()) != null; ) {
1627 +                K k = p.key;
1628 +                V v = p.val;
1629 +                if (function.test(v) && replaceNode(k, null, v) != null)
1630 +                    removed = true;
1631 +            }
1632 +        }
1633 +        return removed;
1634 +    }
1635 +
1636 +    /**
1637       * If the specified key is not already associated with a value,
1638       * attempts to compute its value using the given mapping function
1639       * and enters it into this map unless {@code null}.  The entire
# Line 1628 | Line 1691 | public class ConcurrentHashMap<K,V> exte
1691                          if (fh >= 0) {
1692                              binCount = 1;
1693                              for (Node<K,V> e = f;; ++binCount) {
1694 <                                K ek; V ev;
1694 >                                K ek;
1695                                  if (e.hash == h &&
1696                                      ((ek = e.key) == key ||
1697                                       (ek != null && key.equals(ek)))) {
# Line 1638 | Line 1701 | public class ConcurrentHashMap<K,V> exte
1701                                  Node<K,V> pred = e;
1702                                  if ((e = e.next) == null) {
1703                                      if ((val = mappingFunction.apply(key)) != null) {
1704 +                                        if (pred.next != null)
1705 +                                            throw new IllegalStateException("Recursive update");
1706                                          added = true;
1707                                          pred.next = new Node<K,V>(h, key, val, null);
1708                                      }
# Line 1657 | Line 1722 | public class ConcurrentHashMap<K,V> exte
1722                                  t.putTreeVal(h, key, val);
1723                              }
1724                          }
1725 +                        else if (f instanceof ReservationNode)
1726 +                            throw new IllegalStateException("Recursive update");
1727                      }
1728                  }
1729                  if (binCount != 0) {
# Line 1752 | Line 1819 | public class ConcurrentHashMap<K,V> exte
1819                                  }
1820                              }
1821                          }
1822 +                        else if (f instanceof ReservationNode)
1823 +                            throw new IllegalStateException("Recursive update");
1824                      }
1825                  }
1826                  if (binCount != 0)
# Line 1843 | Line 1912 | public class ConcurrentHashMap<K,V> exte
1912                                  if ((e = e.next) == null) {
1913                                      val = remappingFunction.apply(key, null);
1914                                      if (val != null) {
1915 +                                        if (pred.next != null)
1916 +                                            throw new IllegalStateException("Recursive update");
1917                                          delta = 1;
1918                                          pred.next =
1919                                              new Node<K,V>(h, key, val, null);
# Line 1875 | Line 1946 | public class ConcurrentHashMap<K,V> exte
1946                                      setTabAt(tab, i, untreeify(t.first));
1947                              }
1948                          }
1949 +                        else if (f instanceof ReservationNode)
1950 +                            throw new IllegalStateException("Recursive update");
1951                      }
1952                  }
1953                  if (binCount != 0) {
# Line 1984 | Line 2057 | public class ConcurrentHashMap<K,V> exte
2057                                      setTabAt(tab, i, untreeify(t.first));
2058                              }
2059                          }
2060 +                        else if (f instanceof ReservationNode)
2061 +                            throw new IllegalStateException("Recursive update");
2062                      }
2063                  }
2064                  if (binCount != 0) {
# Line 2001 | Line 2076 | public class ConcurrentHashMap<K,V> exte
2076      // Hashtable legacy methods
2077  
2078      /**
2079 <     * Legacy method testing if some key maps into the specified value
2080 <     * in this table.  This method is identical in functionality to
2079 >     * Tests if some key maps into the specified value in this table.
2080 >     *
2081 >     * <p>Note that this method is identical in functionality to
2082       * {@link #containsValue(Object)}, and exists solely to ensure
2083       * full compatibility with class {@link java.util.Hashtable},
2084 <     * which supported this method prior to introduction of the
2085 <     * Java Collections framework.
2084 >     * which supported this method prior to introduction of the Java
2085 >     * Collections Framework.
2086       *
2087       * @param  value a value to search for
2088       * @return {@code true} if and only if some key maps to the
# Line 2015 | Line 2091 | public class ConcurrentHashMap<K,V> exte
2091       *         {@code false} otherwise
2092       * @throws NullPointerException if the specified value is null
2093       */
2018    @Deprecated
2094      public boolean contains(Object value) {
2095          return containsValue(value);
2096      }
# Line 2163 | Line 2238 | public class ConcurrentHashMap<K,V> exte
2238      /* ---------------- Table Initialization and Resizing -------------- */
2239  
2240      /**
2241 +     * Returns the stamp bits for resizing a table of size n.
2242 +     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
2243 +     */
2244 +    static final int resizeStamp(int n) {
2245 +        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
2246 +    }
2247 +
2248 +    /**
2249       * Initializes table, using the size recorded in sizeCtl.
2250       */
2251      private final Node<K,V>[] initTable() {
# Line 2216 | Line 2299 | public class ConcurrentHashMap<K,V> exte
2299              s = sumCount();
2300          }
2301          if (check >= 0) {
2302 <            Node<K,V>[] tab, nt; int sc;
2302 >            Node<K,V>[] tab, nt; int n, sc;
2303              while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
2304 <                   tab.length < MAXIMUM_CAPACITY) {
2304 >                   (n = tab.length) < MAXIMUM_CAPACITY) {
2305 >                int rs = resizeStamp(n);
2306                  if (sc < 0) {
2307 <                    if (sc == -1 || transferIndex <= 0 ||
2308 <                        (nt = nextTable) == null)
2307 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2308 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2309 >                        transferIndex <= 0)
2310                          break;
2311 <                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
2311 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2312                          transfer(tab, nt);
2313                  }
2314 <                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
2314 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2315 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2316                      transfer(tab, null);
2317                  s = sumCount();
2318              }
# Line 2238 | Line 2324 | public class ConcurrentHashMap<K,V> exte
2324       */
2325      final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
2326          Node<K,V>[] nextTab; int sc;
2327 <        if ((f instanceof ForwardingNode) &&
2327 >        if (tab != null && (f instanceof ForwardingNode) &&
2328              (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
2329 <            while (transferIndex > 0 && nextTab == nextTable &&
2330 <                   (sc = sizeCtl) < -1) {
2331 <                if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1)) {
2329 >            int rs = resizeStamp(tab.length);
2330 >            while (nextTab == nextTable && table == tab &&
2331 >                   (sc = sizeCtl) < 0) {
2332 >                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2333 >                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
2334 >                    break;
2335 >                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
2336                      transfer(tab, nextTab);
2337                      break;
2338                  }
# Line 2280 | Line 2370 | public class ConcurrentHashMap<K,V> exte
2370              }
2371              else if (c <= sc || n >= MAXIMUM_CAPACITY)
2372                  break;
2373 <            else if (tab == table &&
2374 <                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
2375 <                transfer(tab, null);
2373 >            else if (tab == table) {
2374 >                int rs = resizeStamp(n);
2375 >                if (U.compareAndSwapInt(this, SIZECTL, sc,
2376 >                                        (rs << RESIZE_STAMP_SHIFT) + 2))
2377 >                    transfer(tab, null);
2378 >            }
2379          }
2380      }
2381  
# Line 2337 | Line 2430 | public class ConcurrentHashMap<K,V> exte
2430                      sizeCtl = (n << 1) - (n >>> 1);
2431                      return;
2432                  }
2433 <                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
2434 <                    if (sc != -1)
2433 >                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
2434 >                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
2435                          return;
2436                      finishing = advance = true;
2437                      i = n; // recheck before commit
# Line 2535 | Line 2628 | public class ConcurrentHashMap<K,V> exte
2628       * too small, in which case resizes instead.
2629       */
2630      private final void treeifyBin(Node<K,V>[] tab, int index) {
2631 <        Node<K,V> b; int n, sc;
2631 >        Node<K,V> b; int n;
2632          if (tab != null) {
2633 <            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
2634 <                if (tab == table && (sc = sizeCtl) >= 0 &&
2542 <                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
2543 <                    transfer(tab, null);
2544 <            }
2633 >            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
2634 >                tryPresize(n << 1);
2635              else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
2636                  synchronized (b) {
2637                      if (tabAt(tab, index) == b) {
# Line 2608 | Line 2698 | public class ConcurrentHashMap<K,V> exte
2698          final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
2699              if (k != null) {
2700                  TreeNode<K,V> p = this;
2701 <                do  {
2701 >                do {
2702                      int ph, dir; K pk; TreeNode<K,V> q;
2703                      TreeNode<K,V> pl = p.left, pr = p.right;
2704                      if ((ph = p.hash) > h)
# Line 2701 | Line 2791 | public class ConcurrentHashMap<K,V> exte
2791                                    (kc = comparableClassFor(k)) == null) ||
2792                                   (dir = compareComparables(kc, k, pk)) == 0)
2793                              dir = tieBreakOrder(k, pk);
2794 <                            TreeNode<K,V> xp = p;
2794 >                        TreeNode<K,V> xp = p;
2795                          if ((p = (dir <= 0) ? p.left : p.right) == null) {
2796                              x.parent = xp;
2797                              if (dir <= 0)
# Line 2739 | Line 2829 | public class ConcurrentHashMap<K,V> exte
2829          private final void contendedLock() {
2830              boolean waiting = false;
2831              for (int s;;) {
2832 <                if (((s = lockState) & WRITER) == 0) {
2832 >                if (((s = lockState) & ~WAITER) == 0) {
2833                      if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
2834                          if (waiting)
2835                              waiter = null;
# Line 2764 | Line 2854 | public class ConcurrentHashMap<K,V> exte
2854           */
2855          final Node<K,V> find(int h, Object k) {
2856              if (k != null) {
2857 <                for (Node<K,V> e = first; e != null; e = e.next) {
2857 >                for (Node<K,V> e = first; e != null; ) {
2858                      int s; K ek;
2859                      if (((s = lockState) & (WAITER|WRITER)) != 0) {
2860                          if (e.hash == h &&
2861                              ((ek = e.key) == k || (ek != null && k.equals(ek))))
2862                              return e;
2863 +                        e = e.next;
2864                      }
2865                      else if (U.compareAndSwapInt(this, LOCKSTATE, s,
2866                                                   s + READER)) {
# Line 3053 | Line 3144 | public class ConcurrentHashMap<K,V> exte
3144  
3145          static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
3146                                                     TreeNode<K,V> x) {
3147 <            for (TreeNode<K,V> xp, xpl, xpr;;)  {
3147 >            for (TreeNode<K,V> xp, xpl, xpr;;) {
3148                  if (x == null || x == root)
3149                      return root;
3150                  else if ((xp = x.parent) == null) {
# Line 3168 | Line 3259 | public class ConcurrentHashMap<K,V> exte
3259              return true;
3260          }
3261  
3262 <        private static final sun.misc.Unsafe U;
3262 >        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
3263          private static final long LOCKSTATE;
3264          static {
3265              try {
3175                U = sun.misc.Unsafe.getUnsafe();
3176                Class<?> k = TreeBin.class;
3266                  LOCKSTATE = U.objectFieldOffset
3267 <                    (k.getDeclaredField("lockState"));
3268 <            } catch (Exception e) {
3267 >                    (TreeBin.class.getDeclaredField("lockState"));
3268 >            } catch (ReflectiveOperationException e) {
3269                  throw new Error(e);
3270              }
3271          }
# Line 3268 | Line 3357 | public class ConcurrentHashMap<K,V> exte
3357          }
3358  
3359          /**
3360 <         * Save traversal state upon encountering a forwarding node.
3360 >         * Saves traversal state upon encountering a forwarding node.
3361           */
3362          private void pushState(Node<K,V>[] t, int i, int n) {
3363              TableStack<K,V> s = spare;  // reuse if possible
# Line 3284 | Line 3373 | public class ConcurrentHashMap<K,V> exte
3373          }
3374  
3375          /**
3376 <         * Possibly pop traversal state
3376 >         * Possibly pops traversal state.
3377           *
3378           * @param n length of current table
3379           */
# Line 3405 | Line 3494 | public class ConcurrentHashMap<K,V> exte
3494          public K getKey()        { return key; }
3495          public V getValue()      { return val; }
3496          public int hashCode()    { return key.hashCode() ^ val.hashCode(); }
3497 <        public String toString() { return key + "=" + val; }
3497 >        public String toString() {
3498 >            return Helpers.mapEntryToString(key, val);
3499 >        }
3500  
3501          public boolean equals(Object o) {
3502              Object k, v; Map.Entry<?,?> e;
# Line 4425 | Line 4516 | public class ConcurrentHashMap<K,V> exte
4516          }
4517  
4518          public final boolean removeAll(Collection<?> c) {
4519 +            if (c == null) throw new NullPointerException();
4520              boolean modified = false;
4521              for (Iterator<E> it = iterator(); it.hasNext();) {
4522                  if (c.contains(it.next())) {
# Line 4436 | Line 4528 | public class ConcurrentHashMap<K,V> exte
4528          }
4529  
4530          public final boolean retainAll(Collection<?> c) {
4531 +            if (c == null) throw new NullPointerException();
4532              boolean modified = false;
4533              for (Iterator<E> it = iterator(); it.hasNext();) {
4534                  if (!c.contains(it.next())) {
# Line 4616 | Line 4709 | public class ConcurrentHashMap<K,V> exte
4709              throw new UnsupportedOperationException();
4710          }
4711  
4712 +        public boolean removeIf(Predicate<? super V> filter) {
4713 +            return map.removeValueIf(filter);
4714 +        }
4715 +
4716          public Spliterator<V> spliterator() {
4717              Node<K,V>[] t;
4718              ConcurrentHashMap<K,V> m = map;
# Line 4685 | Line 4782 | public class ConcurrentHashMap<K,V> exte
4782              return added;
4783          }
4784  
4785 +        public boolean removeIf(Predicate<? super Entry<K,V>> filter) {
4786 +            return map.removeEntryIf(filter);
4787 +        }
4788 +
4789          public final int hashCode() {
4790              int h = 0;
4791              Node<K,V>[] t;
# Line 6201 | Line 6302 | public class ConcurrentHashMap<K,V> exte
6302      }
6303  
6304      // Unsafe mechanics
6305 <    private static final sun.misc.Unsafe U;
6305 >    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
6306      private static final long SIZECTL;
6307      private static final long TRANSFERINDEX;
6308      private static final long BASECOUNT;
6309      private static final long CELLSBUSY;
6310      private static final long CELLVALUE;
6311 <    private static final long ABASE;
6311 >    private static final int ABASE;
6312      private static final int ASHIFT;
6313  
6314      static {
6315          try {
6215            U = sun.misc.Unsafe.getUnsafe();
6216            Class<?> k = ConcurrentHashMap.class;
6316              SIZECTL = U.objectFieldOffset
6317 <                (k.getDeclaredField("sizeCtl"));
6317 >                (ConcurrentHashMap.class.getDeclaredField("sizeCtl"));
6318              TRANSFERINDEX = U.objectFieldOffset
6319 <                (k.getDeclaredField("transferIndex"));
6319 >                (ConcurrentHashMap.class.getDeclaredField("transferIndex"));
6320              BASECOUNT = U.objectFieldOffset
6321 <                (k.getDeclaredField("baseCount"));
6321 >                (ConcurrentHashMap.class.getDeclaredField("baseCount"));
6322              CELLSBUSY = U.objectFieldOffset
6323 <                (k.getDeclaredField("cellsBusy"));
6324 <            Class<?> ck = CounterCell.class;
6323 >                (ConcurrentHashMap.class.getDeclaredField("cellsBusy"));
6324 >
6325              CELLVALUE = U.objectFieldOffset
6326 <                (ck.getDeclaredField("value"));
6327 <            Class<?> ak = Node[].class;
6328 <            ABASE = U.arrayBaseOffset(ak);
6329 <            int scale = U.arrayIndexScale(ak);
6326 >                (CounterCell.class.getDeclaredField("value"));
6327 >
6328 >            ABASE = U.arrayBaseOffset(Node[].class);
6329 >            int scale = U.arrayIndexScale(Node[].class);
6330              if ((scale & (scale - 1)) != 0)
6331 <                throw new Error("data type scale not a power of two");
6331 >                throw new Error("array index scale not a power of two");
6332              ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
6333 <        } catch (Exception e) {
6333 >        } catch (ReflectiveOperationException e) {
6334              throw new Error(e);
6335          }
6336 +
6337 +        // Reduce the risk of rare disastrous classloading in first call to
6338 +        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
6339 +        Class<?> ensureLoaded = LockSupport.class;
6340      }
6341   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines