JDK-5073546 : Minor ConcurrentHashMap constructor spec tweaks
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2004-07-13
  • Updated: 2017-05-16
  • Resolved: 2005-09-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availabitlity Release.

To download the current JDK release, click here.
JDK 6
6 b51Fixed
Description
The problem:

ConcurrentHashMap's constructors have a number of weaknesses.
The only way to create a map with a non-default concurrency level is
to use the constructor

ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)

Unfortunately, there is no guidance for what the value of loadFactor might mean
exactly, and no way to determine the default value.  A user who wants to use
a concurrencyLevel of 1 would have to read the source to discover that the
default initialCapacity and loadFactor are 16 and .75, respectively.

The javadoc for constructor

ConcurrentHashMap(Map<? extends K, ? extends V> t) 

states 
     * The
     * map is created with a capacity of twice the number of mappings in
     * the given map or 11 (whichever is greater)

First of all, this is a bad idea, since an access pattern that repeatedly
"copies" maps using
ConcurrentHashMap m = new ConcurrentHashMap(oldmap)
will cause exponential waste of space.

Secondly, the implementation does the more sensible thing of using a
more rational initial capacity, just enough to hold the elements of
the source map.  The magic number 11 should be replaced by the
default initial capacity of 16 since it is counterintuitive that
in the sequence

ConcurrentHashMap m1 = new ConcurrentHashMap();
ConcurrentHashMap m2 = new ConcurrentHashMap(m1);

m2 would have a smaller capacity than m1.

###@###.### 2004-07-13

Comments
SUGGESTED FIX --- /tmp/geta21916 2004-07-13 13:54:33.565696600 -0700 +++ ConcurrentHashMap.java 2004-07-13 13:54:29.405306000 -0700 @@ -602,43 +602,45 @@ for (int i = 0; i < this.segments.length; ++i) this.segments[i] = new Segment<K,V>(cap, loadFactor); } /** * Creates a new, empty map with the specified initial - * capacity, and with default load factor and concurrencyLevel. + * capacity, and with default load factor (<tt>0.75f</tt>) + * and concurrencyLevel (<tt>16</tt>). * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements. * @throws IllegalArgumentException if the initial capacity of * elements is negative. */ public ConcurrentHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); } /** - * Creates a new, empty map with a default initial capacity, - * load factor, and concurrencyLevel. + * Creates a new, empty map with a default initial capacity + * (<tt>16</tt>), load factor (<tt>0.75f</tt>) and + * concurrencyLevel (<tt>16</tt>). */ public ConcurrentHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); } /** * Creates a new map with the same mappings as the given map. The - * map is created with a capacity of twice the number of mappings in - * the given map or 11 (whichever is greater), and a default load factor - * and concurrencyLevel. + * map is created with a capacity consistent with the default load + * factor (<tt>0.75f</tt>) and uses the default concurrencyLevel + * (<tt>16</tt>). * @param t the map */ public ConcurrentHashMap(Map<? extends K, ? extends V> t) { this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, - 11), + DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); putAll(t); } // inherit Map javadoc public boolean isEmpty() { final Segment[] segments = this.segments;
2004-07-16

PUBLIC COMMENTS -
2004-07-16

EVALUATION One can understand ConcurrentHashMap better if one realizes that the design is an engineering compromise between incompatible goals: - to have a "modern" design with as much hiding of implementation as possible. - to be a "drop-in replacement" for Hashtable, with as few caveats as possible. Given the conflicting goals, it seems best to explicitly specify the default values in the javadoc, so that a user who wants a concurrencyLevel of 1 with otherwise default values can do: new ConcurrentHashMap(16, 0.75, 1) ###@###.### 2004-07-13
2004-07-13