支持检索的完全并发性和更新的高预期并发性的哈希表。 这个类服从相同功能规范如Hashtable
,并且包括对应于每个方法的方法版本Hashtable
。 不过,尽管所有操作都是线程安全的,检索操作并不意味着锁定,并没有为防止所有访问的方式锁定整个表的任何支持。 这个类可以在依赖于线程安全性的程序中与Hashtable
完全互Hashtable
,但不依赖于其同步细节。 检索操作(包括get
)通常不阻止,因此可能与更新操作重叠(包括put
和remove
)。 检索反映了最近完成的更新操作的结果。 (更正式地,对于给定密钥的更新操作熊之前发生与任何(非空关系)检索该键报告经更新的值。)对于聚合操作,比如putAll
和clear
,并发检索可能反映插入或移除只有一些条目。 类似地,迭代器,分割器和枚举返回在反映迭代器/枚举创建过程中或之后反映哈希表状态的元素。 他们不抛出ConcurrentModificationException
。 然而,迭代器被设计为一次只能由一个线程使用。 请记住,骨料状态方法的结果,包括size
, isEmpty
和containsValue
通常是有用的,只有当一个地图没有发生在其他线程并发更新。 否则,这些方法的结果反映了可能足以用于监视或估计目的的瞬态状态,但不适用于程序控制。
当存在太多的冲突(即,具有不同的哈希码但是以表的大小为模数落入相同的时隙的密钥)时,该表被动态扩展,并且每个映射保持大致两个bin的预期平均效果(对应于0.75负载因素阈值调整大小)。 由于映射被添加和删除,这个平均值可能会有很大差异,但是总的来说,这为哈希表保留了普遍接受的时间/空间权衡。 然而,调整这个或任何其他类型的散列表可能是相对较慢的操作。 如果可能,最好提供一个尺寸估计作为可选的initialCapacity
构造函数参数。 附加的可选的loadFactor
构造函数参数提供了另外的手段,通过指定在计算给定数量的元素时要分配的空间量时使用的表密度来定制初始表容量。 此外,为了与此类的先前版本兼容,构造函数可以可选地指定预期的concurrencyLevel
作为内部大小调整的附加提示。 请注意,使用完全相同的许多键hashCode()
是降低任何哈希表的hashCode()
的一种可靠的方法。 为了改善影响,当按键为Comparable
时,该类可以使用键之间的比较顺序来帮助打破关系。
甲Set
投影一个的ConcurrentHashMap可以(使用被创建newKeySet()
或newKeySet(int)
),或观察(使用keySet(Object)
时仅键是感兴趣的,并且被映射的值是(可能瞬时)不使用或全部取相同的映射值。
ConcurrentHashMap可以通过使用LongAdder
值并通过computeIfAbsent
进行初始化,将其用作可缩放的频率映射(直方图或多集的形式)。 例如,要向ConcurrentHashMap<String,LongAdder> freqs
添加计数,可以使用freqs.computeIfAbsent(k -> new LongAdder()).increment();
此类及其视图和迭代器实现所有的可选方法Map
个Iterator
接口。
像Hashtable
但不像HashMap
,这个类不允许 null
用作键或值。
ConcurrentHashMaps支持一组顺序和并行批量操作,与大多数Stream
方法不同,它们被设计为安全并且经常明智地应用,即使是由其他线程同时更新的映射; 例如,当计算共享注册表中的值的快照摘要时。 有三种操作,每种具有四种形式,接受键,值,条目和(键,值)参数和/或返回值的函数。 由于ConcurrentHashMap的元素不以任何特定的方式排序,并且可能会在不同的并行执行中以不同的顺序进行处理,因此提供的函数的正确性不应取决于任何排序,也不应依赖于可能瞬时变化的任何其他对象或值计算进行中; 除了每一个行动,理想情况下都是无副作用的。 对Map.Entry
对象的批量操作不支持方法setValue
。
- forEach:对每个元素执行给定的操作。 变量形式在执行操作之前对每个元素应用给定的变换。
- search:返回在每个元素上应用给定函数的第一个可用非空结果; 当找到结果时跳过进一步的搜索。
- reduce:累积每个元素。 提供的减少功能不能依赖于排序(更正式地,它应该是关联和交换)。 有五种变体:
- 平原减少 (由于没有相应的返回类型,因此(key,value)函数参数没有这种方法的形式)
- 映射的减少积累了应用于每个元素的给定函数的结果。
- 使用给定的基础值减少到标量双,长和int。
这些批量操作接受一个parallelismThreshold
参数。 如果估计当前地图大小小于给定阈值,则方法依次进行。 使用Long.MAX_VALUE
的值Long.MAX_VALUE
抑制所有的并行性。 使用1
的值可以通过划分为足够的子任务来完全利用用于所有并行计算的ForkJoinPool.commonPool()
来实现最大并行度。 通常,您最初将选择其中一个极值,然后测量使用中间值之间的性能,从而降低开销与吞吐量之间的关系。
批量操作的并发属性遵循ConcurrentHashMap的并发属性:从get(key)
返回的任何非空结果和相关的访问方法与相关的插入或更新都有一个发生之前的关系。 任何批量操作的结果都反映了这些每个元素关系的组成(但是除非以某种方式已知是静态的,它们并不一定是相对于整个地图的原子)。 相反,因为映射中的键和值从不为空,所以null作为目前缺乏任何结果的可靠原子指标。 为了保持此属性,null用作所有非标量缩减操作的隐含基础。 对于double,long和int版本,基础应该是当与任何其他值组合时返回其他值(更正式地,它应该是减少的标识元素)。 大多数常见的减少具有这些属性; 例如,使用基数0或最小值与基准MAX_VALUE计算和。
作为参数提供的搜索和转换函数应该类似地返回null以指示缺少任何结果(在这种情况下不被使用)。 在映射缩减的情况下,这也使得转换可以用作过滤器,如果不应该组合元素,返回null(或者在原始专业化的情况下,身份基础)。 在使用它们进行搜索或减少操作之前,您可以通过在“null意味着现在没有任何内容”规则下自行构建复合转换和过滤。
接受和/或返回Entry参数的方法维护键值关联。 例如,当找到最大价值的钥匙时,它们可能是有用的。 请注意,可以使用new AbstractMap.SimpleEntry(k,v)
提供“plain”Entry new AbstractMap.SimpleEntry(k,v)
。
批量操作可能突然完成,抛出在应用程序中遇到的异常。 在处理这样的异常时,请注意,其他并发执行的函数也可能引发异常,或者如果没有发生第一个异常,则会这样做。
与顺序形式相比,加速比是常见的,但不能保证。 如果并行计算的基础工作比计算本身更昂贵,则涉及小地图上的简短功能的并行操作可能比顺序形式执行得更慢。 类似地,如果所有处理器正忙于执行不相关的任务,并行化可能不会导致太多的实际并行。
所有任务方法的所有参数都必须为非空值。