Java 中的 ConcurrentMap 接口
ConcurrentMap 是 Java Collections Framework 的成员之一,它被引入 JDK 1.5 中,表示一种 Map,它能够处理并发访问,而不会影响 map 条目的一致性。ConcurrentMap 接口位于 java.util.concurrent 包中,除了从超级接口 java.util.Map 继承的方法外,还提供了一些额外的方法。它继承了嵌套接口 Map.Entry<K, V>。
HashMap 操作不是同步的,而 Hashtable 提供了同步。虽然 Hashtable 是线程安全的,但它不太高效。为了解决这个问题,Java Collections Framework 在 Java 1.5中引入了 ConcurrentMap 。
ConcurrentMap 的层次结构

声明:
public interface ConcurrentMap<K,V> extends Map<K,V>
这里, K 是键 Object 的类型,而 V 是值 Object 的类型。
- 它继承 java.util.Map 接口。
- ConcurrentNavigableMap<K,V> 是子接口。
- ConcurrentMap 由 ConcurrentHashMap、 ConcurrentHashMapSkipListMap 类实现。
- ConcurrentMap 被称为同步 Map。
实现类
由于它属于 java.util.concurrent 包,我们必须使用以下导入语句导入它
import java.util.concurrent.ConcurrentMap
or
import java.util.concurrent.*
ConcurrentMap 有两个实现类, ConcurrentSkipListMap 和 ConcurrentHashMap 。ConcurrentSkipListMap 是 ConcurrentNavigableMap 接口的可扩展实现,该接口扩展了 ConcurrentMap 接口。ConcurrentSkipListMap 中的键通过自然顺序或在对象构造时使用比较器进行排序。ConcurrentSkipListMap 在插入、删除和搜索操作的时间成本期望为 log(n) ,它是一个线程安全的类,因此,所有基本操作均可以并发完成。
语法:
// ConcurrentHashMap 实现的 ConcurrentMap
CocurrentMap<K, V> numbers = new ConcurrentHashMap<K, V>();
// ConcurrentSkipListMap 实现的 ConcurrentMap
ConcurrentMap< ? , ? > objectName = new ConcurrentSkipListMap< ? , ? >();
例子:
// 演示 ConcurrentMap 接口的方法的 Java 程序
import java.util.concurrent.*;
class ConcurrentMapDemo {
public static void main(String[] args) {
// 由于 ConcurrentMap 是一个接口,所以我们使用 ConcurrentHashMap 创建实例
ConcurrentMap<Integer, String> m = new ConcurrentHashMap<Integer, String>();
m.put(100, "Geeks");
m.put(101, "For");
m.put(102, "Geeks");
// 因为 101 键已经存在,所以我们不能添加 Hello
m.putIfAbsent(101, "Hello");
// 我们可以删除条目,因为 101 键关联 For 值
m.remove(101, "For");
// 现在我们可以添加 Hello
m.putIfAbsent(101, "Hello");
// 我们可以用 For 替换 Hello
m.replace(101, "Hello", "For");
System.out.println("Map contents : " + m);
}
}
输出
映射内容:{100=Geeks,101=For,102=Geeks}
基本方法
1. 添加元素
ConcurrentSkipListMap的put()方法是Java中的内置函数,它将指定的值与此映射中的指定键相关联。如果地图先前包含键的映射,则替换旧值。
// Java程序示例添加
// elements
import java.util.concurrent.*;
class AddingElementsExample {
public static void main(String[] args)
{
//实例化对象
//因为ConcurrentMap
//是一个接口,所以我们使用
//ConcurrentSkipListMap
ConcurrentMap<Integer,Integer> mpp = new ConcurrentSkipListMap<Integer,Integer>();
//使用put()方法将元素添加到此映射中
为(int i =1; i <= 5; i ++)
mpp.put(i,i);
//将映射打印到控制台
System.out.println(“ put()后:“+ mpp);
}
} ```
输出
put()后:{1 = 1,2 = 2,3 = 3,4 = 4,5 = 5}
2. 删除元素
ConcurrentSkipListMap的remove()方法是Java中的内置函数,它从此地图中删除指定键的映射。如果该特定键的映射不存在,则该方法返回null。执行此方法后,地图的大小会减小。
// Java程序示例删除
// elements
import java.util.concurrent.*;
class RemovingElementsExample {
public static void main(String[] args)
{
//实例化对象
//因为ConcurrentMap
//是一个接口,所以我们使用
//ConcurrentSkipListMap
ConcurrentMap<Integer,Integer> mpp = new ConcurrentSkipListMap<Integer,Integer>();
//使用put方法将元素添加到该映射中
为(int i =1; i <= 5; i ++)
mpp.put(i,i);
//删除与key 1关联的打印映射
mpp.remove(1);
System.out.println(“ remove()后:“+ mpp);
}
} ```
输出
remove()后:{2 = 2,3 = 3,4 = 4,5 = 5}
3. 访问元素
我们可以使用get()方法访问ConcurrentSkipListMap的元素,下面给出示例。
// Java程序演示访问
// elements
import java.util.concurrent.*;
class AccessingElementsExample {
public static void main(String[] args)
{
//实例化对象
//因为ConcurrentMap
//是一个接口,所以我们使用
//ConcurrentSkipListMap
ConcurrentMap<Integer,String> chm = new ConcurrentSkipListMap<Integer,String>();
//使用put方法插入映射
chm.put(100,“Geeks”);
chm.put(101,“for”);
chm.put(102,“Geeks”);
chm.put(103,“Contribute”);
//显示HashMap
System.out.println(“ Mappings are:“);
System.out.println(chm);
//显示100的值
System.out.println(“与
+“ 100相关的值为:“+ chm.get(100));
//获取103的值
System.out.println(“与
+“ 103相关的值为:“+ chm.get(103));
}
} ```
输出
映射为:
{100 = Geeks,101 = for,102 = Geeks,103 = Contribute}
与100相关联的值是:Geeks
与103相关的值为:Contribute
4. 遍历
我们可以使用Iterator接口来遍历Collection Framework的任何结构。由于Iterator使用一种数据类型,我们要使用Entry< ? , ? >将两个不同的类型解析为兼容的格式。然后,使用next()方法输出ConcurrentSkipListMap的元素。
import java.util.concurrent.*;
import java.util.*;
public class TraversingExample {
public static void main(String[] args)
{
// 实例化对象
// 由于ConcurrentMap是一个接口,因此我们使用ConcurrentSkipListMap
ConcurrentMap<Integer, String> chmap = new ConcurrentSkipListMap<Integer, String>();
// 使用put()添加元素
chmap.put(8, "Third");
chmap.put(6, "Second");
chmap.put(3, "First");
chmap.put(11, "Fourth");
// 在ConcurrentSkipListMap上创建一个Iterator
Iterator<ConcurrentSkipListMap
.Entry<Integer, String> > itr
= chmap.entrySet().iterator();
// 使用hasNext()方法来检查下一个元素是否存在。使用next()方法来检索下一个元素。
while (itr.hasNext()) {
ConcurrentSkipListMap
.Entry<Integer, String> entry
= itr.next();
System.out.println("Key = " + entry.getKey()
+ ", Value = "
+ entry.getValue());
}
}
}
输出
Key = 3, Value = First
Key = 6, Value = Second
Key = 8, Value = Third
Key = 11, Value = Fourth
ConcurrentMap的方法
- K - 映射内键的类型。
- V - 映射内值的类型。
| 方法 | 描述 |
|---|---|
| compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 尝试为指定的键及其当前映射值(如果没有当前映射,则为null)计算映射。 |
| computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) | 如果指定的键尚未与值相关联(即映射到null),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非为null。 |
| computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 如果指定键的值存在且非null,则尝试计算给定键及其当前映射值的新映射。 |
| forEach(BiConsumer<? super K,? super V> action) | 对此映射中的每个条目执行给定操作,直到处理完所有条目或操作引发异常。 |
| getOrDefault(Object key, V defaultValue) | 返回指定键映射到的值,如果此映射不包含该键的映射,则返回defaultValue。 |
| merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) | 如果指定键尚未关联到值或关联到null,则将其与给定的非null值关联。 |
| putIfAbsent(K key, V value) | 如果指定键尚未关联到值,则将其与给定值关联。 |
| remove(Object key, Object value) | 仅当当前映射到给定值时,才删除键的条目。 |
| replace(K key, V value) | 仅当当前映射到某个值时,才替换键的条目。 |
| replace(K key, V oldValue, V newValue) | 仅当当前映射到给定值时,才替换键的条目。 |
| replaceAll(BiFunction<? super K,? super V,? extends V> function) | 将每个条目的值替换为对该条目调用给定函数的结果,直到处理完所有条目或函数引发异常。 |
从接口java.util.Map继承的方法
方法 | 描述
—|—
clear() | 从该映射中删除所有映射(可选操作)。
containsKey(Object key) | 如果此映射包含指定键的映射,则返回true。
containsValue(Object value) | 如果该映射将一个或多个键映射到指定值,则返回true。
entry(K k, V v) | 返回包含给定键和值的不可变的Map.Entry。
entrySet() | 返回此映射中包含的映射的Set视图。
equals(Object o) | 将指定对象与此映射进行比较以实现相等性。
get(Object key) | 返回指定键所映射的值;如果此映射不包含此键的映射,则返回null。
hashCode() | 返回此映射的哈希码值。
isEmpty() | 如果此映射不包含键值对映射,则返回true。
keySet() | 返回此映射中包含的键的Set视图。
of() | 返回一个包含零个映射的不可变映射。
of(K k1, V v1) | 返回一个包含单个映射的不可变映射。
of(K k1, V v1, K k2, V v2) | 返回一个包含两个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3) | 返回一个包含三个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) | 返回一个包含四个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) | 返回一个包含五个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) | 返回一个包含六个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) | 返回一个包含七个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) | 返回一个包含八个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) | 返回一个包含九个映射的不可变映射。
of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) | 返回一个包含十个映射的不可变映射。
ofEntries(Map.Entry<? extends K,? extends V>… entries) | 返回从给定条目中提取的键和值组成的不可变映射。
put(K key, V value) | 将指定值与指定键在此映射中进行关联(可选操作)。
putAll(Map<? extends K,? extends V> m) | 将指定映射中的所有映射复制到此映射中(可选操作)。
remove(Object key) | 如果存在,从此映射中移除具有指定键的映射(可选操作)。
size() | 返回此映射中键值对映射的数量。
size() | 返回此映射中的键值对数量。
参考资料: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ConcurrentMap.html
极客教程