Wend看源码-Java-Map学习

news2024/12/29 10:41:56

摘要

        在当今的编程世界中,深入了解各类数据类型对于开发者而言至关重要。本篇聚焦于 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"对应的值,由于存在该键的映射,返回映射的值10
        Integer value1 = map.getOrDefault("key1", 5);
        System.out.println("getOrDefault for key1: " + value1);
        // 使用getOrDefault获取"key2"对应的值,不存在该键的映射,返回默认值5
        Integer 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不会改变其值,返回已有的值5
        Integer existingValue = mapForPutIfAbsent.putIfAbsent("key5", 6);
        System.out.println("putIfAbsent for existing key5: " + existingValue);
        // 对于不存在的"key6"键,putIfAbsent会添加新的键值对,并返回null
        Integer 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会替换其值,并返回旧值7
        Integer replacedValue = mapForReplace.replace("key7", 8);
        System.out.println("replace for existing key7: " + replacedValue);
        // 对于不存在的"key8"键,replace返回null
        Integer 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根据键和旧值计算新值并替换,返回新值12
        Integer computedIfPresentValue = mapForComputeIfPresent.computeIfPresent("key11", (k, v) -> v + 1);
        System.out.println("computeIfPresent for existing key11: " + computedIfPresentValue);
        // 对于不存在的"key12"键,computeIfPresent返回null
        Integer 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根据键和旧值计算新值并替换,返回新值14
        Integer computedNewValue = mapForCompute.compute("key13", (k, v) -> v + 1);
        System.out.println("compute for existing key13: " + computedNewValue);
        // 对于不存在的"key14"键,compute根据计算结果添加新的键值对(这里示例计算返回固定值15),返回新值15
        Integer 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",值为1
        Map<String, Integer> singleEntryMap = Map.of("one", 1);
        System.out.println("Single Entry Map: " + singleEntryMap);

        // 创建一个包含2个映射关系的不可变映射,注意重复键会抛出异常,这里键分别为"two"、"three",值为2、3
        try {
            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":6
        Map<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",值为9
        Map.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);

        // 创建一个本身就是不可变映射(这里利用之前创建的entriesMap)
        Map<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> {
    @Override
    public 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.coloradmin.cn/o/2267408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp开发微信小程序实现获取“我的位置”

1. 创建GetLocation项目 使用HBuilder X创建一个项目GetLocation,使用Vue3。 2. 在腾讯地图开放平台中创建应用 要获取位置,在小程序中需要使用腾讯地图或是高德地图。下面以腾讯地图为例。 (1)打开腾讯地图开放平台官方网址:腾讯位置服务 - 立足生态,连接未来 (2)注册…

如何使用 Firewalld 设置 Linux 防火墙

简介 在 Linux 中&#xff0c;防火墙是一个应用程序或服务&#xff0c;它使用预定的规则集来过滤或控制进出 Linux 服务器的网络流量。通常将防火墙配置为允许所需的流量通过&#xff0c;同时阻止其余的外部流量。 Firewalld 是一个用于 Linux 系统的防火墙管理解决方案。它是…

财务域——业财一体设计

摘要 本文讨论了业财一体化的概念、背景、目标、技术推动因素以及企业经营环境的复杂化。业财一体化旨在通过信息化手段实现业务与财务数据的实时共享与联动&#xff0c;提升管理效率和决策能力。文章还概述了业财一体化在销售管理、成本核算、预算管理和风险管控等方面的应用…

【时间之外】IT人求职和创业应知【80】-特殊日子

目录 北京冬季招聘会 OpenAI CEO炮轰马斯克 英伟达推出全新AI芯片B300 莫欢喜&#xff0c;总成空。本周必须要谨行慎言。 感谢所有打开这个页面的朋友。人生不如意&#xff0c;开越野车去撒野&#xff0c;会害了自己&#xff0c;不如提升自己。提升自己的捷径就是学习和思考…

【AIGC-ChatGPT副业提示词指令 - 动图】魔法咖啡馆:一个融合创意与治愈的互动体验设计

引言 在当今快节奏的生活中&#xff0c;咖啡早已不仅仅是提神醒脑的饮品&#xff0c;更成为了一种情感寄托和生活态度的表达。本文将介绍一个独特的"魔法咖啡馆"互动体验设计&#xff0c;通过将咖啡与情感、魔法元素相结合&#xff0c;创造出一个充满想象力和治愈感…

Docker使用——国内Docker的安装办法

文章目录 参考资料前言Mac安装办法Homebrew 安装1. 直接下报错2. 安装homebrew&#xff0c; 用国内镜像3. 安装Docker4. 启动docker服务5. 测试是否安装成功 参考资料 鸣谢大佬文章。 macOS系统中&#xff1a;Docker的安装&#xff1a;https://blog.csdn.net/sulia1234567890…

python opencv的sift特征检测(Scale-Invariant Feature Transform)

sift 官方文档地址&#xff1a;https://docs.opencv.org/4.10.0/da/df5/tutorial_py_sift_intro.html 创建SIFT实例cv2.SIFT.create()特征检测sift.detect描述子sift.compute/sift.detectAndCompute画特征cv2.drawKeypoints 原图 特征点 代码 import cv2first ./12.pngsif…

精读DeepSeek v3技术文档的心得感悟

最近宋大宝同学读完了DeepSeekv3的文档&#xff0c;心中颇多感慨&#xff0c;忍不住想在这里记录一下对这款“业界有望启示未来低精度训练走向”的开源大模型的观察与思考。DeepSeek v3的亮点绝不仅仅是“Float8”或“超长上下文”这么简单&#xff0c;而是贯穿了从数值精度、注…

yarn install 安装报错:Workspaces can only be enabled in private projects.

在本地运行项目的时候&#xff0c;使用yarn install 安装模块依赖的时候&#xff0c;遇到报错&#xff1a;Workspaces can only be enabled in private projects. 一、原因分析 报这个错误是因为你使用了yarn的workspace&#xff0c;但并未将工程标记为private。 二、解决办法 …

深度学习中的残差网络、加权残差连接(WRC)与跨阶段部分连接(CSP)详解

随着深度学习技术的不断发展&#xff0c;神经网络架构变得越来越复杂&#xff0c;而这些复杂网络在训练时常常遇到梯度消失、梯度爆炸以及计算效率低等问题。为了克服这些问题&#xff0c;研究者们提出了多种网络架构&#xff0c;包括 残差网络&#xff08;ResNet&#xff09;、…

Excel将混乱的多行做成1列

目标是将数据按从左到右&#xff0c;再从上到下排成一列。 公式法 首先用textjoin函数将文本包起来&#xff0c;做成一个超长文本。 然后用公式 截取文本 Mid(m1,n,3)&#xff0c;意思就是对m1单元格&#xff0c;从第n个字符开始&#xff0c;截取3个字符出来。 这个公式如何自…

在vscode的ESP-IDF中使用自定义组件

以hello-world为例&#xff0c;演示步骤和注意事项 1、新建ESP-IDF项目 选择模板 从hello-world模板创建 2、打开项目 3、编译结果没错 正在执行任务: /home/azhu/.espressif/python_env/idf5.1_py3.10_env/bin/python /home/azhu/esp/v5.1/esp-idf/tools/idf_size.py /home…

基于springboot校园招聘系统源码和论文

可做计算机毕业设计JAVA、PHP、爬虫、APP、小程序、C#、C、python、数据可视化、大数据、文案 使用旧方法对校园招聘系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在校园招聘系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比…

PaddleOCR文字识别模型的FineTune

一、paddleOCR paddle框架为百度开发的深度学习框架&#xff0c;其中对于文字检测、识别具有较为便利的开发条件。同时PaddleOCR文字识别工具较为轻量化&#xff0c;并可按照任务需求进行model的finetune&#xff0c;满足实际的业务需求。 源码来源&#xff1a;githubOCR 在gi…

【数据库初阶】Ubuntu 环境安装 MySQL

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; 数据库初阶 &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 Ubuntu 系统安装 MySQL 的相关内容。 如果看到最后您觉得这篇…

MoH:将多头注意力(Multi-Head Attention)作为头注意力混合(Mixture-of-Head Attention)

摘要 https://arxiv.org/pdf/2410.11842? 在本文中&#xff0c;我们对Transformer模型的核心——多头注意力机制进行了升级&#xff0c;旨在提高效率的同时保持或超越先前的准确度水平。我们表明&#xff0c;多头注意力可以表示为求和形式。鉴于并非所有注意力头都具有同等重…

AI助力古诗视频制作全流程化教程

AI助力古诗视频制作全流程化教程 目录 1. 制作视频的原材料&#xff08;全自动&#xff09; 2.文生图&#xff1a;图像生成&#xff08;手动&#xff09; 3.文生音频&#xff1a;TTS技术&#xff08;全自动&#xff09; 4.视频编辑&#xff08;手动&#xff09; 5.自动发…

基于SSM的“快递管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“快递管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登陆页面 注册页面 快递员页面 派单员订单管理页面 派单员订单添…

AWTK 在全志 tina linux 上支持 2D 图形加速

全志 tina linux 2D 图形加速插件。 开发环境为 全志 Tina Linux 虚拟机。 1. 准备 下载 awtk git clone https://github.com/zlgopen/awtk.git下载 awtk-linux-fb git clone https://github.com/zlgopen/awtk-linux-fb.git下载 awtk-tina-g2d git clone https://github.co…

Unity游戏环境交互系统

概述 交互功能使用同一个按钮或按钮列表&#xff0c;在不同情况下显示不同的内容&#xff0c;按下执行不同的操作。 按选项个数分类 环境交互系统可分为两种&#xff0c;单选项交互&#xff0c;一般使用射线检测&#xff1b;多选项交互&#xff0c;一般使用范围检测。第一人…