在本教程中,我们将借助示例学习Java ConcurrentHashMap类及其操作。
Java集合框架的ConcurrentHashMap类提供了线程安全的映射。 也就是说,多个线程可以一次访问该映射,而不会影响映射中条目的一致性。
它继承了ConcurrentMap接口。
为了创建并发的哈希图,我们必须先导入java.util.concurrent.ConcurrentHashMap包。导入包后,就可以在Java中创建并发哈希映射。
//ConcurrentHashMap具有容量8和负载因子0.6 ConcurrentHashMap<Key, Value> numbers = new ConcurrentHashMap<>(8, 0.6f);
在上面的代码中,我们创建了一个名为numbers的并发哈希映射。
这里,
Key - 用于关联map中每个元素(值)的唯一标识符
Value - map中与键相关联的元素
注意语句 new ConcurrentHashMap<>(8, 0.6)。在这里,第一个参数是capacity,第二个参数是loadFactor。
capacity -该映射的容量为8。意味着,它可以存储8个条目。
loadFactor-此map的负载因子为0.6。这意味着,只要我们的哈希表填充了60%,条目就会移到新哈希表中,其大小是原始哈希表的两倍。
默认容量和负载因子
无需定义其容量和负载因子就可以创建并发哈希图。例如,
// 具有默认容量和负载因子的ConcurrentHashMap ConcurrentHashMap<Key, Value> numbers1 = new ConcurrentHashMap<>();
默认,
map的容量将为 16
负载因子将为 0.75
这是我们如何创建包含其他映射的所有元素的并发哈希映射。
import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; class Main { public static void main(String[] args) { // 创建偶数的hashmap HashMap<String, Integer> evenNumbers = new HashMap<>(); evenNumbers.put("Two", 2); evenNumbers.put("Four", 4); System.out.println("HashMap: " + evenNumbers); //从其他映射创建并发hashmap ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(evenNumbers); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); } }
输出结果
HashMap: {Four=4, Two=2} ConcurrentHashMap: {Four=4, Two=2, Three=3}
ConcurrentHashMap类提供了允许我们在映射上执行各种操作的方法。
put() - 将指定的键/值映射插入到映射中
putAll() - 将指定映射的所有条目插入此map
putIfAbsent() - 如果映射中不存在指定的键,则将指定的键/值映射插入到map中
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { //创建偶数的ConcurrentHashMap ConcurrentHashMap<String, Integer> evenNumbers = new ConcurrentHashMap<>(); // 使用 put() evenNumbers.put("Two", 2); evenNumbers.put("Four", 4); // 使用 putIfAbsent() evenNumbers.putIfAbsent("Six", 6); System.out.println("偶数的ConcurrentHashMap: " + evenNumbers); //Creating ConcurrentHashMap of numbers ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); // 使用 putAll() numbers.putAll(evenNumbers); System.out.println("ConcurrentHashMap的数字为: " + numbers); } }
输出结果
偶数的ConcurrentHashMap: {Six=6, Four=4, Two=2} ConcurrentHashMap的数字为: {Six=6, One=1, Four=-4, Two=2}
1.使用entrySet(),keySet()和values()
entrySet() - 返回一组所有键/值映射的集合
keySet() - 返回map所有键的集合
values() - 返回map所有值的集合
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); // 使用 entrySet() System.out.println("Key/Value 映射: " + numbers.entrySet()); // 使用 keySet() System.out.println("Keys: " + numbers.keySet()); // 使用 values() System.out.println("Values: " + numbers.values()); } }
输出结果
ConcurrentHashMap: {One=1, Two=2, Three=3} Key/Value 映射: [One=1, Two=2, Three=3] Keys: [One, Two, Three] Values: [1, 2, 3]
2.使用get()和getOrDefault()
get() - 返回与指定键关联的值。如果找不到键,则返回null。
getOrDefault() - 返回与指定键关联的值。如果找不到键,则返回指定的默认值。
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); // 使用 get() int value1 = numbers.get("Three"); System.out.println("使用get(): " + value1); // 使用 getOrDefault() int value2 = numbers.getOrDefault("Five", 5); System.out.println("使用getOrDefault(): " + value2); } }
输出结果
ConcurrentHashMap: {One=1, Two=2, Three=3} 使用get(): 3 使用getOrDefault(): 5
remove(key) - 返回并从映射中删除与指定键关联的条目
remove(key, value) - 仅当指定键映射到指定值并返回布尔值时,才从映射中删除条目
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); //单参数删除方法 int value = numbers.remove("Two"); System.out.println("被删除的值: " + value); // 具有两个参数的删除方法 boolean result = numbers.remove("Three", 3); System.out.println("条目 {Three=3} 被删除? " + result); System.out.println("更新后的ConcurrentHashMap: " + numbers); } }
输出结果
ConcurrentHashMap: {One=1, Two=2, Three=3} 被删除的值: 2 条目 {Three=3} 被删除? True 更新后的ConcurrentHashMap: {One=1}
ConcurrentHashMap类提供了可以安全地应用于并行的map不同的批量操作方法。
forEach()方法遍历我们的条目并执行指定的函数。
它包含两个参数。
parallelismThreshold -它指定在映射中并行执行多少个元素操作之后。
transformer -这将在将数据传递到指定函数之前转换数据。
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); //forEach()不包含传递的函数 numbers.forEach(4, (k, v) -> System.out.println("key: " + k + " value: " + v)); // forEach()传递指定函数 System.out.print("Values are "); numbers.forEach(4, (k, v) -> v, (v) -> System.out.print(v + ", ")); } }
输出结果
ConcurrentHashMap: {One = 1, Two = 2, Three = 3} key: One value: 1 key: Two value: 2 key: Three value: 3 Values are 1, 2, 3,
在上面的程序中,我们使用了并行阈值4。这意味着,如果映射包含4个条目,则该操作将并行执行。
forEach()方法的变体
forEachEntry() - 为每个条目执行指定的函数
forEachKey() - 为每个键执行指定的函数
forEachValue() - 为每个值执行指定的函数
search()方法基于指定的函数搜索map并返回匹配的条目。
这里,指定的函数决定搜索什么条目。
它还包含一个可选参数parallelThreshold。并行阈值指定在映射中有多少元素之后并行执行操作。
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); // 使用 search() String key = numbers.search(4, (k, v) -> {return v == 3 ? k: null;}); System.out.println("被搜索的值: " + key); } }
输出结果
ConcurrentHashMap: {One=1, Two=2, Three=3} 被搜索的值: Three
search()方法的变体
searchEntries() - 搜索函数应用于键/值映射
searchKeys() - 搜索函数仅适用于按键
searchValues() - 搜索函数仅应用于值
reduce()方法累积(聚集)映射中的每个条目。 当我们需要所有条目来执行一项常见任务(例如添加映射的所有值)时,可以使用此方法。
它包含两个参数。
parallelismThreshold -它指定在有多少元素之后,map中的操作将并行执行。
transformer - 这将在数据传递给指定函数之前对数据进行转换。
例如,
import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); numbers.put("Two", 2); numbers.put("Three", 3); System.out.println("ConcurrentHashMap: " + numbers); // 使用 search() int sum = numbers.reduce(4, (k, v) -> v, (v1, v2) -> v1 + v2); System.out.println("所有值的总和: " + sum); } }
输出结果
ConcurrentHashMap: {One=1, Two=2, Three=3} 所有值的总和: 6
在上面的程序中,请注意以下语句
numbers.reduce(4, (k, v) -> v, (v1, v2) -> v1+v2);
这里,
4 是并行阈值
(k, v) -> v是一个转换函数。它将键/值映射仅转换为值。
(v1, v2) -> v1+v2 是计算大小函数。它收集所有值并将所有值相加。
reduce()方法的变体
reduceEntries() - 返回使用指定的reducer函数收集所有条目的结果
reduceKeys() - 返回使用指定的reducer函数收集所有键的结果
reduceValues() - 返回使用指定的reducer函数收集所有值的结果
以下是ConcurrentHashMap和HashMap之间的一些区别,
ConcurrentHashMap是线程安全的集合。也就是说,多个线程可以同时访问和修改它。
ConcurrentHashMap提供用于批量操作的方法,例如forEach(),search()和reduce()。
ConcurrentHashMap类允许多个线程修改操作并发进行。
默认情况下,并发哈希映射分为16段。这就是为什么允许16个线程同时修改映射的原因。但是,一次可以访问任意数量的线程。
如果指定的键已经存在,则putIfAbsent()方法将不会覆盖映射中的条目。