Wend看源码-Java-Map学习

server/2024/12/31 21:40:38/

摘要

        在当今的编程世界中,深入了解各类数据类型对于开发者而言至关重要。本篇聚焦于 JDK 21 版本下,Java.util 包所提供的 Map 类型。Map 作为一种关键的数据结构,能够以键值对的形式高效存储和检索数据,广泛应用于众多领域。

        本文将简要概述 Map 数据类型在 JDK 21 中的具体实现细节。为了帮助开发者依据不同的应用场景精准选型,我们会分别介绍非线程安全的Map接口及其实现和线程安全的Map接口及其实现。随后本文还将概述 Map 映射类型其广泛的应用场景,助力开发者灵活运用 Map 类型解决各类编程难题,提升代码质量与程序性能。

Java-Map 类图

图1  Java-Map类型数据结构类图(基本)

图2  Java-Map类型数据结构类图(线程安全的)

Dictionary(Deprecated)

  DictionaryMap接口的前身,但功能相对较弱且不够灵活。与现代的Map类似,它也是用于存储键值对,通过键来获取对应的值。不过,它的实现细节比较简单,没有像HashMapMap实现类那样复杂的哈希机制或排序功能。

Map 接口

        Map 接口是所有Map 映射类型实现的父类,为Java提供了一种基于key-value键值对存储的数据类型。以下是关于Map 接口的重要方法总结。

  • size:返回此映射中键值对的数量。若映射包含的元素数量超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE

  • isEmpty:若此映射不包含任何键值对,则返回 true,用于判断映射是否为空。

  • containsKey:判断映射中是否包含指定键的映射关系。如果存在键 k 使得 Objects.equals(key, k) 为 true,则返回 true

  • containsValue:判断映射中是否有一个或多个键映射到指定的值。对于大多数 Map 接口的实现,此操作可能需要线性时间复杂度(与映射大小相关)。

  • get:返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回 null

  • put:将指定的值与指定的键关联(可选操作)。如果映射先前包含该键的映射关系,则旧值会被指定值替换。返回与键关联的先前值,若不存在该键的映射,则返回 null(若实现支持 null 值,返回 null 也可能表示之前将 null 与该键关联)。

  • remove:如果存在指定键的映射关系,则从此映射中移除该映射(可选操作),并返回此映射先前与该键关联的值,若不存在该键的映射,则返回 null

  • putAll:将指定映射中的所有映射关系复制到此映射中(可选操作)。其效果等同于针对指定映射中的每个键值对,依次调用此映射的 put(k, v) 方法。

  • clear:从此映射中移除所有映射关系(可选操作),调用后映射将变为空。

视图相关方法

  • keySet:返回此映射中包含的键的 Set 视图。该集合由映射支持,对映射的更改会反映在集合中,反之亦然。

  • values:返回此映射中包含的值的 Collection 视图。与 keySet 类似,对映射的更改会反映在该集合中,反之亦然,迭代时修改映射可能导致未定义结果,支持通过特定操作移除元素(从而移除映射中对应映射关系),但不支持 add 和 addAll 操作。

  • entrySet:返回此映射中包含的映射关系(键值对)的 Set 视图。同样遵循映射与视图相互影响、迭代时修改需遵循规则等原则,支持通过相关操作移除元素以移除映射中对应关系,不支持 add 和 addAll 操作。

Java8 引入

  • getOrDefault:返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回默认值 defaultValue。默认实现未对同步或原子性属性做保证,若实现要提供原子性保证需重写此方法并记录其并发属性。

  • forEach:对映射中的每个条目执行给定的操作,直到所有条目都被处理或操作抛出异常。默认实现按照条目集迭代顺序(若有指定顺序)执行操作,未保证同步和原子性属性,若有相关要求需重写并记录。若指定的操作 action 为 null,会抛出 NullPointerException;若在迭代期间发现条目被移除,会抛出 ConcurrentModificationException

  • replaceAll:对每个条目的值使用给定函数进行替换,直到所有条目都被处理或函数抛出异常。

  • putIfAbsent:如果指定键尚未与值关联(或映射到 null),则将其与给定值关联并返回 null,否则返回当前值。

  • replace:仅当指定键当前映射到某个值时,才替换该键对应的条目值,返回先前与指定键关联的值,若不存在该键的映射,则返回 null(若实现支持 null 值,返回 null 也可能表示之前将 null 与该键关联)。

  • computeIfAbsent:如果指定键尚未与值关联(或映射到 null),则尝试使用给定的映射函数计算其值并将其放入此映射中(除非计算结果为 null),然后返回与指定键关联的当前值(存在则返回现有值,不存在则可能返回计算后的值或 null)。

  • computeIfPresent:如果指定键的值存在且非 null,则尝试根据键及其当前映射值计算新的映射。若重映射函数返回 null,则移除该映射,返回新的值(若不存在则返回 null)。

  • compute:尝试根据指定键及其当前映射值(或 null,如果不存在当前映射)计算映射。若重映射函数返回 null,则移除映射(若原本不存在则保持不变),返回新的值(若不存在则返回 null)。

  • merge:如果指定键尚未与值关联或关联的值为 null,则将其与给定的非 null 值关联;否则,使用给定的重映射函数替换关联的值,若结果为 null 则移除该映射,返回与指定键关联的新值(若不存在则返回 null)。

Java8 Map方法示例
 /*** Java 8中Map接口新增的方法示例*/private static void Java8MapMethodTest() {// 创建一个HashMap实例,这里使用Map接口来引用它,方便后续切换不同的Map实现类Map<String, Integer> map = new HashMap<>();// 1. getOrDefault方法示例map.put("key1", 10);// 使用getOrDefault获取"key1"对应的值,由于存在该键的映射,返回映射的值10Integer value1 = map.getOrDefault("key1", 5);System.out.println("getOrDefault for key1: " + value1);// 使用getOrDefault获取"key2"对应的值,不存在该键的映射,返回默认值5Integer value2 = map.getOrDefault("key2", 5);System.out.println("getOrDefault for key2: " + value2);// 2. forEach方法示例// 使用forEach遍历map中的每个条目,并打印键值对信息map.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));try {// 故意在迭代过程中通过remove方法移除一个条目,会触发ConcurrentModificationException异常map.forEach((key, value) -> {if ("key1".equals(key)) {map.remove(key);}});} catch (ConcurrentModificationException e) {System.out.println("Caught ConcurrentModificationException as expected: " + e.getMessage());}// 3. replaceAll方法示例Map<String, Integer> mapToReplaceAll = new HashMap<>();mapToReplaceAll.put("key3", 3);mapToReplaceAll.put("key4", 4);// 使用replaceAll方法将每个条目的值替换为原来值的两倍mapToReplaceAll.replaceAll((key, oldValue) -> oldValue * 2);System.out.println("After replaceAll: " + mapToReplaceAll);// 4. putIfAbsent方法示例Map<String, Integer> mapForPutIfAbsent = new HashMap<>();mapForPutIfAbsent.put("key5", 5);// 对于已经存在的"key5"键,putIfAbsent不会改变其值,返回已有的值5Integer existingValue = mapForPutIfAbsent.putIfAbsent("key5", 6);System.out.println("putIfAbsent for existing key5: " + existingValue);// 对于不存在的"key6"键,putIfAbsent会添加新的键值对,并返回nullInteger newKeyValue = mapForPutIfAbsent.putIfAbsent("key6", 6);System.out.println("putIfAbsent for new key6: " + newKeyValue);System.out.println("Map after putIfAbsent operations: " + mapForPutIfAbsent);// 5. replace方法示例Map<String, Integer> mapForReplace = new HashMap<>();mapForReplace.put("key7", 7);// 对于存在的"key7"键,replace会替换其值,并返回旧值7Integer replacedValue = mapForReplace.replace("key7", 8);System.out.println("replace for existing key7: " + replacedValue);// 对于不存在的"key8"键,replace返回nullInteger nonExistentReplacedValue = mapForReplace.replace("key8", 9);System.out.println("replace for non-existent key8: " + nonExistentReplacedValue);System.out.println("Map after replace operations: " + mapForReplace);// 6. computeIfAbsent方法示例Map<String, String> mapForComputeIfAbsent = new HashMap<>();// 对于不存在的"key9"键,使用computeIfAbsent通过给定函数计算值并添加键值对,返回计算后的值"ComputedValue"String computedValue = mapForComputeIfAbsent.computeIfAbsent("key9", k -> "ComputedValue");System.out.println("computeIfAbsent for new key9: " + computedValue);// 对于已经存在的"key10"键(假设已存在,这里示例代码没添加,实际可自行添加测试),computeIfAbsent不会改变其值,返回已有的值String existingComputedValue = mapForComputeIfAbsent.computeIfAbsent("key10", k -> "AnotherComputedValue");System.out.println("computeIfAbsent for existing key10: " + existingComputedValue);System.out.println("Map after computeIfAbsent operations: " + mapForComputeIfAbsent);// 7. computeIfPresent方法示例Map<String, Integer> mapForComputeIfPresent = new HashMap<>();mapForComputeIfPresent.put("key11", 11);// 对于存在且值非null的"key11"键,使用computeIfPresent根据键和旧值计算新值并替换,返回新值12Integer computedIfPresentValue = mapForComputeIfPresent.computeIfPresent("key11", (k, v) -> v + 1);System.out.println("computeIfPresent for existing key11: " + computedIfPresentValue);// 对于不存在的"key12"键,computeIfPresent返回nullInteger nonExistentComputeIfPresentValue = mapForComputeIfPresent.computeIfPresent("key12", (k, v) -> v + 1);System.out.println("computeIfPresent for non-existent key12: " + nonExistentComputeIfPresentValue);System.out.println("Map after computeIfPresent operations: " + mapForComputeIfPresent);// 8. compute方法示例Map<String, Integer> mapForCompute = new HashMap<>();mapForCompute.put("key13", 13);// 对于存在的"key13"键,使用compute根据键和旧值计算新值并替换,返回新值14Integer computedNewValue = mapForCompute.compute("key13", (k, v) -> v + 1);System.out.println("compute for existing key13: " + computedNewValue);// 对于不存在的"key14"键,compute根据计算结果添加新的键值对(这里示例计算返回固定值15),返回新值15Integer computedNewValueForAbsentKey = mapForCompute.compute("key14", (k, v) -> 15);System.out.println("compute for absent key14: " + computedNewValueForAbsentKey);System.out.println("Map after compute operations: " + mapForCompute);// 9. merge方法示例Map<String, String> mapForMerge = new HashMap<>();mapForMerge.put("key15", "Value1");// 对于已存在的"key15"键,使用merge根据重映射函数合并值,返回新值"Value1New"String mergedValue = mapForMerge.merge("key15", "New", (oldValue, newValue) -> oldValue + "New");System.out.println("merge for existing key15: " + mergedValue);// 对于不存在的"key16"键,merge添加新的键值对,返回给定的非null值"NewValue"String mergedValueForNewKey = mapForMerge.merge("key16", "NewValue", (oldValue, newValue) -> oldValue + newValue);System.out.println("merge for new key16: " + mergedValueForNewKey);System.out.println("Map after merge operations: " + mapForMerge);}

Java9 +引入

  • of:返回一个包含X个映射关系的不可变映射。

  • ofEntries:返回一个不可变映射,其包含从给定条目中提取的键和值,条目本身不会存储在映射中。

  • entry:返回一个包含给定键和值的不可变 Entry,适合用于通过 Map.ofEntries 方法填充 Map 实例。

  • copyOf:Java10引入:返回一个包含给定映射中条目的不可变 Map,给定映射不能为 null,且不能包含 null 键或值。若给定映射后续被修改,返回的映射不会反映这些修改。若给定映射是不可变映射,调用此方法通常不会创建副本,会根据具体情况返回原映射或基于其构建的新的不可变映射。

Java9+ Map方法示例
 /*** Java 9中Map接口新增的方法示例*/private static void Java9MapMethodsTest() {// 1. Map.of方法示例// 创建一个包含0个映射关系的不可变映射(空映射)Map<String, Integer> emptyMap = Map.of();System.out.println("Empty Map: " + emptyMap);// 创建一个包含1个映射关系的不可变映射,键为"one",值为1Map<String, Integer> singleEntryMap = Map.of("one", 1);System.out.println("Single Entry Map: " + singleEntryMap);// 创建一个包含2个映射关系的不可变映射,注意重复键会抛出异常,这里键分别为"two"、"three",值为2、3try {Map<String, Integer> twoEntriesMap = Map.of("two", 2, "two", 3);System.out.println("Two Entries Map: " + twoEntriesMap);} catch (IllegalArgumentException e) {System.out.println("Caught IllegalArgumentException for duplicate key: " + e.getMessage());}// 创建一个包含多个(这里以3个为例)映射关系的不可变映射,键值分别为"four":4, "five":5, "six":6Map<String, Integer> multipleEntriesMap = Map.of("four", 4, "five", 5, "six", 6);System.out.println("Multiple Entries Map: " + multipleEntriesMap);// 2. Map.ofEntries方法示例// 先使用Map.entry方法创建多个Entry实例,模拟给定的条目Map.Entry<String, Integer> entry1 = Map.entry("seven", 7);Map.Entry<String, Integer> entry2 = Map.entry("eight", 8);// 使用ofEntries方法基于创建的Entry实例构建不可变映射Map<String, Integer> entriesMap = Map.ofEntries(entry1, entry2);System.out.println("Map created by ofEntries: " + entriesMap);// 3. Map.entry方法示例// 创建一个不可变的Entry实例,键为"nine",值为9Map.Entry<String, Integer> newEntry = Map.entry("nine", 9);System.out.println("Created Entry: " + newEntry);// 注意,尝试创建键或值为null的Entry会抛出NullPointerException,以下代码会报错,注释掉仅作示意// Entry<String, Integer> nullEntry = Map.entry(null, 10);// 4. Map.copyOf方法示例// 创建一个普通的可变映射(这里用HashMap举例)java.util.HashMap<String, Integer> mutableMap = new java.util.HashMap<>();mutableMap.put("ten", 10);// 使用copyOf方法将可变映射转换为不可变映射,注意原映射不能有null键或值,这里符合要求Map<String, Integer> copiedMap = Map.copyOf(mutableMap);System.out.println("Copied Map: " + copiedMap);// 尝试修改原可变映射,添加新的键值对mutableMap.put("eleven", 11);// 再次查看不可变映射,不会反映原可变映射后续的修改System.out.println("Copied Map after modifying original mutable map: " + copiedMap);// 创建一个本身就是不可变映射(这里利用之前创建的entriesMapMap<String, Integer> immutableMap = Map.ofEntries(Map.entry("twelve", 12));// 对不可变映射使用copyOf方法,通常不会创建副本,返回原映射Map<String, Integer> copiedImmutableMap = Map.copyOf(immutableMap);System.out.println("Copied Immutable Map (should be the same as original): " + copiedImmutableMap);}

AbstractMap

  AbstractMap是一个抽象类,它实现了Map接口。在 Java 集合框架中,它为Map接口的实现类提供了一个基本的骨架实现,简化了创建自定义Map实现的过程。


SequencedMap

  SequencedMap用于表示具有明确元素顺序的Map,它在 Java 21 中被引入。这个接口扩展了Map接口,并添加了与顺序相关的操作。

主要功能

  • 获取首尾元素相关操作:它提供了像firstEntry()lastEntry()这样的方法,可以获取映射中的第一个和最后一个键值对。例如,在一个存储学生成绩排名(键为学生姓名,值为成绩)的SequencedMap中,可以很方便地获取成绩最高和最低的学生信息。

  • 元素顺序遍历操作SequencedMap支持正向和反向的迭代顺序。通过reversed()方法可以获取一个反向顺序的视图,这样就可以方便地按照相反的顺序遍历映射中的元素。例如,在一个按照插入顺序存储事件日志(键为时间戳,值为事件详情)的SequencedMap中,如果需要按照时间倒序查看日志,就可以使用reversed()方法。

SortedMap

  SortedMap扩展了SequencedMap接口,用于表示按照键的自然顺序(如果键实现了Comparable接口)或者通过指定的比较器进行排序的Map。这种排序使得在遍历Map或者查找元素时可以按照特定的顺序进行。

主要功能

  • 范围视图获取方法SortedMap提供了获取子映射的方法,如subMap(K fromKey, K toKey),它返回一个包含指定范围内键值对的视图。例如,在一个存储员工工资信息(键为员工姓名,值为工资)的SortedMap中,按照员工姓名排序后,可以通过subMap方法获取某一部门(员工姓名范围)的工资信息。

  • 首尾键获取方法firstKey()lastKey()方法可以获取排序后的第一个和最后一个键。例如,在一个按照时间顺序存储销售记录(键为销售时间,值为销售额)的SortedMap中,通过firstKey()lastKey()可以获取最早和最晚的销售时间。

  • 比较器相关方法:可以通过comparator()方法获取用于排序的比较器。如果是按照键的自然顺序排序,返回null;如果是通过指定的比较器排序,则返回该比较器。

NavigableMap

        NavigableMap扩展了SortedMap接口,它提供了导航方法来遍历和操作排序后的Map。这些导航方法允许更灵活地在排序后的键值对之间进行查找和操作,比如查找最接近给定键的键值对等。

主要方法

  • 导航方法
    • ceilingEntry(K key):返回大于等于给定键的最小键值对。例如,在一个存储商品价格区间(键为价格下限,值为价格上限)的NavigableMap中,通过ceilingEntry可以找到价格大于等于某个指定价格的最小价格区间。

    • floorEntry(K key):返回小于等于给定键的最大键值对。例如,在一个按照时间顺序存储任务完成时间(键为任务编号,值为完成时间)的NavigableMap中,通过floorEntry可以找到完成时间小于等于某个指定时间的最后一个任务。

    • higherEntry(K key):返回大于给定键的最小键值对。

    • lowerEntry(K key):返回小于给定键的最大键值对。

  • 范围查询和操作方法
    • subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive):返回一个子映射,并且可以通过布尔参数指定是否包含起始和结束键。这比SortedMapsubMap方法更灵活。

    • headMap(K toKey, boolean inclusive):返回一个小于(或小于等于,取决于inclusive参数)指定键的子映射。

    • tailMap(K fromKey, boolean inclusive):返回一个大于(或大于等于,取决于inclusive参数)指定键的子映射。

TreeMap

  TreeMap实现了NavigableMap接口。TreeMap基于红黑树(Red - Black Tree)数据结构来存储键值对,这使得它可以按照键的自然顺序(如果键实现了Comparable接口)或者根据自定义的比较器来对键进行排序。

TreeMap 实现示例

  TreeMap<String, Integer> treeMap = new TreeMap<>();treeMap.put("apple", 1);treeMap.put("banana", 2);treeMap.put("cherry", 3);System.out.println("Value of 'apple': " + treeMap.get("apple"));System.out.println("First key: " + treeMap.firstKey());System.out.println("Last key: " + treeMap.lastKey());treeMap.remove("banana");System.out.println("After removal: " + treeMap);

TreeMap 自定义比较器实现示例

class CustomComparator implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}
}class TreeMapWithComparatorExample {public static void main(String[] args) {TreeMap<Integer, String> treeMapWithComparator = new TreeMap<>(new CustomComparator());treeMapWithComparator.put(1, "One");treeMapWithComparator.put(3, "Three");treeMapWithComparator.put(2, "Two");System.out.println("TreeMap with custom comparator: " + treeMapWithComparator);}
}

HashMap

  HashMap它基于哈希表(hash table)数据结构来实现,通过对键进行哈希运算来确定存储位置,从而能够快速地进行插入、删除和查找操作。

  • 哈希桶(Hash Buckets)HashMap内部维护了一个数组,这个数组的每个元素被称为哈希桶。当插入一个键值对时,首先会根据键的哈希值计算出它在数组中的索引位置,然后将键值对存储在对应的哈希桶中。例如,对于一个简单的HashMap<String, Integer>,如果要插入键为 "apple",值为1的键值对,会先计算 "apple" 的哈希值,再根据哈希值确定存储在数组中的哪个位置。

  • 哈希冲突(Hash Collisions)解决机制:当不同的键计算出相同的哈希值(即发生哈希冲突)时,HashMap使用链表(在 Java 8 之后,当链表长度超过一定阈值(默认为 8)时会转换为红黑树结构)来存储这些具有相同哈希值的键值对。这样可以有效地解决哈希冲突,保证数据的存储和检索效率。

LinkedHashMap

  LinkedHashMapHashMap的子类,它在HashMap的基础上,维护了一个双向链表,用于记录键值对的插入顺序或者访问顺序(可以通过构造函数指定)。

  • 双向链表:除了具有HashMap的哈希表结构用于快速存储和检索键值对外,LinkedHashMap内部的双向链表保证了元素的顺序性。当按照插入顺序维护时,每次插入新的键值对,都会将其添加到链表的末尾。当按照访问顺序维护时,每次访问(通过getput等操作访问键)一个键值对,都会将其移动到链表的末尾。

EnumMap

  EnumMap是一个专门为枚举键设计的映射实现。它内部使用数组来实现,这使得它非常高效。

特点

  • 键必须是单个枚举类型的枚举常量。
  • EnumMap在内部以数组形式存储其映射,因此它比基于哈希表的实现(如HashMap)更高效。
  • 它是非同步的。
  • 它不允许使用null键。

使用场景

        当映射的键是枚举类型时,使用EnumMap可以提供更好的性能和内存效率。

IdentityHashMap

 IdentityHashMap是一个特殊的Map实现,它比较键的方式是通过==而不是equals方法。

特点

  • 它使用哈希表实现,但比较键的方式是通过==操作符,而不是equals方法。这意味着两个具有相同值的对象(例如,两个不同的字符串实例,但具有相同的字符序列)被视为不同的键。
  • 它通常用于需要身份敏感的哈希映射操作,例如,当需要区分对象实例时。
  • 它是非同步的。
  • 它允许使用null键和值。

使用场景

        用于需要基于对象身份(而不是对象相等性)的映射。

WeakHashMap

  WeakHashMap是一个基于弱引用的Map实现。

特点

  • 它使用弱引用来存储键。弱引用允许垃圾回收器在没有其他强引用的情况下回收其引用的对象。
  • 当键对象不再被其他地方引用时,它们可以被垃圾回收器回收,从而从WeakHashMap中自动删除对应的映射。
  • 这使得WeakHashMap非常适合缓存实现,因为它可以自动清理不再使用的条目。
  • 它是非同步的。
  • 它允许使用null值,但不允许使用null键。

使用场景

  • 用于缓存,其中需要自动删除不再被外部引用的条目。
  • 用于创建映射,其键的生命周期不受映射本身管理的约束。

线程安全的Map 

HashTable(Deprecated)

  Hashtable继承了Dictionary,是一个线程安全的古老的(从 Java 1.0 开始)键值对存储类。他与HashMap类似,同样基于哈希表的数据结构来存储键值对。但Hashtable是同步的(线程安全的),这意味着在多线程环境下,多个线程访问Hashtable时会进行同步操作,保证数据的一致性,但这也导致了性能上的损失。

ConcurrentMap 接口

  ConcurrentMap是一个接口,它位于java.util.concurrent包中。这个接口用于定义支持并发访问的Map操作。它是java.util.Map接口的子接口,提供了额外的原子操作方法,这些方法在多线程环境下可以安全地执行,无需额外的外部同步。

ConcurrentHashMap

  ConcurrentHashMap实现了ConcurrentMap接口,提供了一种高效的、支持高并发访问的哈希表数据结构。它在保证线程安全的同时,还能提供较好的性能,通过锁分段、CAS等技术来减少多线程访问时的锁竞争。

工作原理
  • 分段锁(Segment Locks)技术(Java 7 及以前):在早期版本(Java 7 及以前)中,ConcurrentHashMap采用了分段锁的设计。它将整个哈希表分为多个段(Segment),每个段是一个独立的哈希表,并且有自己的锁。当多个线程访问不同段的键值对时,它们可以并发地进行操作,只有当多个线程访问同一个段中的键值对时,才会产生锁竞争。例如,假设有一个ConcurrentHashMap被分为 16 个段,那么最多可以允许 16 个线程同时进行插入、删除或查找操作,只要它们操作的键值对位于不同的段中。

  • CAS(Compare - and - Swap)操作和数组扩容优化(Java 8 及以后):从 Java 8 开始,ConcurrentHashMap的内部结构进行了优化。它采用了数组 + 链表 / 红黑树的结构,类似于HashMap,但在并发控制方面做了更多的改进。它使用 CAS 操作来实现无锁的插入和更新操作,减少了锁的使用。同时,在扩容时,它采用了更加高效的方式,允许多个线程同时协助进行扩容操作,提高了扩容的效率。

ConcurrentNavigableMap 接口

  ConcurrentNavigableMap是一个扩展了ConcurrentMapNavigableMap接口,它提供了导航操作,并保持并发访问的安全性。

ConcurrentSkipListMap

 ConcurrentSkipListMap实现了ConcurrentNavigableMap接口。它基于跳表(Skip List)数据结构实现,提供了一种可以高效地进行并发访问的有序映射。跳表是一种类似于平衡树的数据结构,它通过多层索引来提高查找效率。

工作原理
  • 跳表数据结构:跳表由多个层次的链表组成。最底层的链表包含了所有的键值对,每一层链表都是下一层链表的一个子集,并且通过 “跳跃” 指针来跨越一些节点,从而加快查找速度。在ConcurrentSkipListMap中,通过使用锁和 CAS 操作来保证在多线程环境下的并发安全。当插入一个新的键值对时,可能会涉及到对多个层次链表的更新,这些更新操作是原子性的,以确保数据的一致性。

对比总结

        如果需要有序的映射,则ConcurrentSkipListMap是一个很好的选择;如果不需要排序,则ConcurrentHashMap通常提供更好的性能。

参考文献

豆包

相关文章推荐

Wend看源码-Java-集合学习(List)-CSDN博客
Wend看源码-Java-集合学习(Set)-CSDN博客
Wend看源码-Java-集合学习(Queue)-CSDN博客


http://www.ppmy.cn/server/154288.html

相关文章

keepass实现google自输入_SSH_TELNET_RDP联动

涉及到的是使用开源密码管理工具KeePass结合特定插件实现自动化密码填充的功能&#xff0c;特别是在谷歌浏览器中的应用。KeePass是一款强大的密码管理软件&#xff0c;它允许用户安全地存储各种账号的用户名和密码&#xff0c;并通过加密保护这些敏感信息。 1. keepass安装及配…

SVN和Git

SVN&#xff08;Subversion&#xff09;和 Git 都是流行的版本控制系统&#xff08;VCS&#xff09;&#xff0c;但它们在架构、使用场景、功能等方面有所不同。以下是它们的主要区别、各自的好处以及如何使用它们的详细说明。 一、SVN 和 Git 的区别 1. 版本控制模型 SVN&…

spring-面试整理

简述 spring中如何基于注解添加bean 和装配bean 1、首先要在Spring中配置开启注解扫描 2、在具体的类上增加具体的注解 3、spring中通常使用autowired 或者是Resource等注解进行bean的装配 总结&#xff1a; 装配流程&#xff1a; 1、实例化&#xff1a;spring容器根据配置或者…

前端经典面试合集(二)——Vue/React/Node/工程化工具/计算机网络

1. 说说 Vue 中的 Diff 算法 Vue 的 Diff 算法 主要用于优化虚拟 DOM 和实际 DOM 之间的比较过程。它通过以下几种策略来提高性能&#xff1a; 最小化对 DOM 的操作&#xff1a;Vue 通过在内存中构建一个虚拟 DOM 树&#xff0c;在虚拟 DOM 树与真实 DOM 树之间进行比较和更新…

JVMTI 笔记

JVMTI(JVM tool interface)是一套c/c开发接口&#xff0c;用于对JVM进行性能分析、debug、内存管理、线程分析等各种黑科技操作 JVMTI开发1个CPU Profiler&#xff1a; agent.c JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {jvmtiEnv *…

探索 .idea 文件夹:Java Maven 工程的隐形守护者

一、.idea文件夹深度解析&#xff1a;IntelliJ IDEA项目配置的核心 在Java Maven工程的开发环境中&#xff0c;.idea文件夹扮演着举足轻重的角色。这是IntelliJ IDEA项目特有的一个配置文件夹&#xff0c;它包含了项目所需的各种配置信息&#xff0c;以确保项目能够在不同的开…

【JavaEE进阶】Spring传递请求参数

目录 &#x1f38d;序言 &#x1f334;传递单个参数 &#x1f340;传递多个参数 &#x1f384;传递对象 &#x1f333;后端参数重命名&#xff08;后端参数映射&#xff09; &#x1f6a9;ReuqestParam注解 &#x1f38d;序言 访问不同的路径,就是发送不同的请求.在发送…

如何在 Ubuntu 22.04 上安装 Elasticsearch

简介 在本教程中&#xff0c;你将学习如何在 Ubuntu 22.04 服务器上安装 Elasticsearch。此外&#xff0c;你还将学习如何使用 Elasticsearch REST API 索引和操作数据。 Elasticsearch 是一个基于 Apache Lucene Library 的免费分布式搜索和分析引擎。它是一个快速且可扩展的…