Java Map常用方法和实现类深度详解

Java Map常用方法和实现类深度详解
在这里插入图片描述

文章目录

在这里插入图片描述

前言

在Java集合框架中,Map是最核心、最常用的数据结构之一。与Collection体系下的List、Set不同,Map采用**键值对(Key-Value)**的存储方式,每个键映射到一个值,键在同一个Map中不可重复。这种设计使得Map特别适合需要通过键快速查找值的场景,如缓存系统、配置管理、数据索引等。

本文将从Map接口的设计哲学出发,深入剖析HashMap、LinkedHashMap、TreeMap、Hashtable、ConcurrentHashMap等主要实现类的底层原理、源码实现、性能特性,并结合Java 8+的新特性,帮助读者全面掌握Map的使用技巧和选型策略。


第一章 Map接口概述

1.1 Map的继承体系

Java中的Map体系是一个独立于Collection的并行框架,其核心继承结构如下:

Map (interface) ├── HashMap (class) │ └── LinkedHashMap (class) ├── TreeMap (class) ├── Hashtable (class) │ └── Properties (class) └── ConcurrentMap (interface) └── ConcurrentHashMap (class) 

1.2 Map的核心特性

  • 键唯一性:每个键最多映射到一个值,键的不可重复性通过equals()hashCode()保证
  • 值可重复:不同的键可以对应相同的值
  • 元素无序:大部分Map实现(如HashMap)不保证元素的顺序
  • 允许null:HashMap允许一个null键和多个null值,但Hashtable和ConcurrentHashMap不允许

1.3 存储结构的理解

从数据结构角度看,Map的存储可以分为三个层面:

  1. key视角:所有key构成一个Set集合 → 无序、不可重复,key所在的类必须重写equals()hashCode()
  2. value视角:所有value构成一个Collection集合 → 无序、可重复,value所在的类需要重写equals()
  3. entry视角:每个key-value对构成一个Entry对象,所有entry构成一个Set集合 → 无序、不可重复

这种设计体现了Map与Set、List的内在联系,也为后续的遍历操作奠定了基础。


第二章 HashMap:最常用的Map实现

HashMap是基于哈希表实现的Map,它根据键的hashCode值存储数据,具有O(1)的平均查找时间,是日常开发中使用频率最高的Map实现。

2.1 底层数据结构演进

HashMap的底层实现经历了从JDK 7到JDK 8的重要优化:

版本底层结构节点类型特点
JDK 7数组 + 链表Entry头插法,扩容时可能产生循环链表
JDK 8+数组 + 链表 + 红黑树Node/TreeNode尾插法,链表长度>8且数组长度>64时树化

2.2 核心源码深度解析

2.2.1 重要成员变量
// 默认初始容量16,必须是2的n次幂staticfinalint DEFAULT_INITIAL_CAPACITY =1<<4;// 最大容量staticfinalint MAXIMUM_CAPACITY =1<<30;// 默认负载因子0.75staticfinalfloat DEFAULT_LOAD_FACTOR =0.75f;// 链表转红黑树阈值staticfinalint TREEIFY_THRESHOLD =8;// 红黑树转链表阈值staticfinalint UNTREEIFY_THRESHOLD =6;// 树化最小数组容量staticfinalint MIN_TREEIFY_CAPACITY =64;
2.2.2 设计哲学解读

为什么默认负载因子是0.75?

负载因子表示散列表的空间使用程度。0.75是时间与空间的折中选择:

  • 过高(如1):空间利用率高,但Hash碰撞概率增加,链表变长,查询效率下降
  • 过低(如0.5):Hash碰撞减少,查询快,但空间浪费严重

为什么容量必须是2的n次幂?

这涉及HashMap的核心优化:

  1. 高效取模:计算数组下标时,(n - 1) & hash等价于hash % n,位运算速度远快于取模
  2. 均匀分布:2^n-1的二进制全是1,与运算结果能充分利用hash值的所有位,减少碰撞
  3. 扩容优化:扩容后元素的新位置要么在原位置,要么在原位置+旧容量,只需看hash值新增位是0还是1

为什么链表转红黑树的阈值是8?

这是基于泊松分布的概率统计。在理想随机hashCode下,链表节点数出现的概率遵循泊松分布,节点数为8的概率接近千万分之六,此时链表查询性能已经很差,转为红黑树可以挽回性能。而树节点占用的空间是普通节点的两倍,当节点数降到6时再转回链表,避免频繁转换。

2.3 put方法执行流程

HashMap的put方法是理解其工作原理的关键入口:

finalVputVal(int hash,K key,V value,boolean onlyIfAbsent,boolean evict){Node<K,V>[] tab;Node<K,V> p;int n, i;// 1. 数组延迟初始化:首次put时创建数组if((tab = table)==null||(n = tab.length)==0) n =(tab =resize()).length;// 2. 计算下标,如果该位置为空直接插入if((p = tab[i =(n -1)& hash])==null) tab[i]=newNode(hash, key, value,null);else{Node<K,V> e;K k;// 3. 处理Hash冲突if(p.hash == hash &&((k = p.key)== key ||(key !=null&& key.equals(k)))) e = p;// 第一个节点就是要找的keyelseif(p instanceofTreeNode) e =((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);// 红黑树插入else{// 链表遍历for(int binCount =0;;++binCount){if((e = p.next)==null){ p.next =newNode(hash, key, value,null);// 尾插法if(binCount >= TREEIFY_THRESHOLD -1)treeifyBin(tab, hash);// 检查是否需要树化break;}if(e.hash == hash &&((k = e.key)== key ||(key !=null&& key.equals(k))))break; p = e;}}// 4. 找到相同key,替换valueif(e !=null){V oldValue = e.value;if(!onlyIfAbsent || oldValue ==null) e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;// 5. 检查是否需要扩容if(++size > threshold)resize();afterNodeInsertion(evict);returnnull;}

执行流程总结

  1. 计算key的hash值(扰动函数:高16位与低16位异或)
  2. 通过(n - 1) & hash计算数组下标
  3. 如果该位置为空,直接插入
  4. 如果该位置不为空,遍历链表或红黑树
  5. 找到相同key则替换value,否则插入新节点
  6. 检查是否需要树化或扩容

2.4 扩容机制(resize)

当元素个数超过threshold = capacity * loadFactor时,HashMap会进行扩容:

finalNode<K,V>[]resize(){Node<K,V>[] oldTab = table;int oldCap =(oldTab ==null)?0: oldTab.length;int oldThr = threshold;int newCap, newThr =0;// 计算新容量if(oldCap >0){if(oldCap >= MAXIMUM_CAPACITY){ threshold =Integer.MAX_VALUE;return oldTab;}// 容量翻倍elseif((newCap = oldCap <<1)< MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr <<1;// 阈值也翻倍}// ... 初始化逻辑// 创建新数组@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab =(Node<K,V>[])newNode[newCap]; table = newTab;// 数据迁移if(oldTab !=null){for(int j =0; j < oldCap;++j){Node<K,V> e;if((e = oldTab[j])!=null){ oldTab[j]=null;if(e.next ==null)// 单个节点直接重新计算下标 newTab[e.hash &(newCap -1)]= e;elseif(e instanceofTreeNode)// 红黑树拆分((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else{// 链表拆分:保持原顺序Node<K,V> loHead =null, loTail =null;Node<K,V> hiHead =null, hiTail =null;Node<K,V> next;do{ next = e.next;// 关键优化:根据hash值新增位判断新位置if((e.hash & oldCap)==0){if(loTail ==null) loHead = e;else loTail.next = e; loTail = e;}else{if(hiTail ==null) hiHead = e;else hiTail.next = e; hiTail = e;} e = next;}while(e !=null);if(loTail !=null){ loTail.next =null; newTab[j]= loHead;// 原索引位置}if(hiTail !=null){ hiTail.next =null; newTab[j + oldCap]= hiHead;// 原索引+旧容量}}}}}return newTab;}

扩容优化点

  • JDK 8采用尾插法,避免JDK 7头插法在多线程环境下产生的循环链表问题
  • 元素迁移时,无需重新计算hash,只需看e.hash & oldCap是否为0,为0则留在原位,否则移到原位置+oldCap
  • 链表保持原顺序,不会倒置

2.5 线程安全问题

HashMap是线程不安全的,多线程环境下可能出现以下问题:

  1. 数据覆盖:两个线程同时put,计算出的下标相同,一个线程插入的数据可能被另一个覆盖
  2. size不准确++size操作非原子性,多个线程同时put可能导致size偏小
  3. JDK 7扩容死循环:头插法在并发扩容时可能形成环形链表,导致CPU 100%

解决方案:

  • 使用Collections.synchronizedMap(new HashMap<>())
  • 使用ConcurrentHashMap推荐

第三章 LinkedHashMap:保持插入顺序

LinkedHashMap继承自HashMap,在HashMap基础上通过双向链表维护元素的顺序。

3.1 数据结构特点

staticclassEntry<K,V>extendsHashMap.Node<K,V>{Entry<K,V> before, after;// 前驱和后继指针Entry(int hash,K key,V value,Node<K,V> next){super(hash, key, value, next);}}

LinkedHashMap在HashMap的Node基础上增加了beforeafter指针,构成了一个双向链表,用于记录元素的插入顺序或访问顺序。

3.2 两种排序模式

LinkedHashMap支持两种迭代顺序:

  1. 插入顺序(默认):按元素首次插入Map的顺序迭代
  2. 访问顺序:按元素最近被访问(get/put)的时间从旧到新迭代
// 指定访问顺序Map<String,String> map =newLinkedHashMap<>(16,0.75f,true); map.put("a","1"); map.put("b","2"); map.get("a");// 访问a,a会被移动到链表尾部// 迭代顺序:b, a(最近访问的在最后)

3.3 实现LRU缓存

利用访问顺序模式,可以轻松实现LRU(Least Recently Used)缓存

classLRUCache<K,V>extendsLinkedHashMap<K,V>{privatefinalint maxCapacity;publicLRUCache(int maxCapacity){super(16,0.75f,true);// 启用访问顺序this.maxCapacity = maxCapacity;}@OverrideprotectedbooleanremoveEldestEntry(Map.Entry<K,V> eldest){returnsize()> maxCapacity;// 超过容量时移除最久未访问的元素}}

3.4 性能特点

  • 遍历速度:只与元素个数有关,与HashMap容量无关,因此当HashMap容量大而实际元素少时,LinkedHashMap遍历更快
  • 插入性能:略低于HashMap,因为需要维护双向链表
  • 内存占用:比HashMap多两个指针的开销

第四章 TreeMap:基于红黑树的排序Map

TreeMap实现了SortedMapNavigableMap接口,底层基于红黑树实现,能够对键进行排序。

4.1 排序机制

TreeMap要求键要么实现Comparable接口(自然排序),要么在构造时提供Comparator(定制排序):

// 自然排序:键必须实现ComparableTreeMap<Integer,String> naturalMap =newTreeMap<>();// 定制排序:提供ComparatorTreeMap<String,Integer> customMap =newTreeMap<>((s1, s2)-> s2.compareTo(s1)// 降序);

4.2 核心方法

TreeMap提供了丰富的导航方法:

TreeMap<Integer,String> map =newTreeMap<>(); map.put(1,"one"); map.put(3,"three"); map.put(5,"five"); map.put(7,"seven");Integer firstKey = map.firstKey();// 1Integer lastKey = map.lastKey();// 7Integer lowerKey = map.lowerKey(5);// 3(小于5的最大键)Integer floorKey = map.floorKey(4);// 3(小于等于4的最大键)Integer ceilingKey = map.ceilingKey(4);// 5(大于等于4的最小键)Integer higherKey = map.higherKey(5);// 7(大于5的最小键)// 子Map视图SortedMap<Integer,String> headMap = map.headMap(5);// 键<5的部分SortedMap<Integer,String> tailMap = map.tailMap(5);// 键>=5的部分SortedMap<Integer,String> subMap = map.subMap(3,6);// 3<=键<6

4.3 源码分析:compare方法

TreeMap的核心是比较逻辑,它在putgetremove等操作中都会用到:

finalintcompare(Object k1,Object k2){return comparator ==null?((Comparable<?superK>)k1).compareTo((K)k2): comparator.compare((K)k1,(K)k2);}

如果既没有提供Comparator,键也没有实现Comparable,在插入时会抛出ClassCastException

4.4 注意事项

  1. 键不能为null:因为无法比较null
  2. compareTo与equals需一致:当两个键比较结果为0时,TreeMap认为它们相等,即使equals返回false

字符串键的特殊性:字符串的compareTo基于Unicode值,数字字符串排序时需注意

// 错误:字符串排序按字典序,"22"会排在"5"前面TreeMap<String,Integer> map =newTreeMap<>(); map.put("5",1); map.put("22",2);// 实际顺序:22, 5// 正确:转为整数比较TreeMap<String,Integer> map =newTreeMap<>((a, b)->Integer.parseInt(a)-Integer.parseInt(b));

第五章 Hashtable与Properties

5.1 Hashtable:古老的线程安全Map

Hashtable是JDK 1.0就存在的古老实现类,具有以下特点:

  • 线程安全:所有方法都用synchronized修饰
  • 不允许null键和null值:否则抛出NullPointerException
  • 初始容量11,扩容为2*old+1
  • 性能较低:全表锁导致并发性能差
Hashtable<String,Integer> table =newHashtable<>(); table.put("key",1);// table.put(null, 2); // 运行时异常

性能对比

  • 写入速度:Hashtable可能比HashMap快(测试数据:1420ms vs 797ms)
  • 读取速度:HashMap比Hashtable快(188ms vs 265ms)

5.2 Properties:处理配置文件

Properties继承自Hashtable,专门用于处理配置文件,键和值都是String类型。

Properties props =newProperties(); props.setProperty("url","jdbc:mysql://localhost:3306/db"); props.setProperty("username","root"); props.setProperty("password","123456");// 加载配置文件try(InputStream input =newFileInputStream("config.properties")){ props.load(input);String url = props.getProperty("url");String username = props.getProperty("username");}

常用方法:

  • load(InputStream) / store(OutputStream):加载/存储配置文件
  • getProperty(String key, String defaultValue):获取属性,可指定默认值
  • list(PrintStream):打印所有属性

第六章 ConcurrentHashMap:并发编程的利器

ConcurrentHashMap是Java并发包(java.util.concurrent)中提供的线程安全且高性能的Map实现。

6.1 设计哲学

ConcurrentHashMap的设计目标是:在保证线程安全的同时,提供比Hashtable更高的并发性能

实现类锁策略并发度性能
Hashtable全表锁极低
Collections.synchronizedMap全表锁极低
ConcurrentHashMap JDK 7分段锁16
ConcurrentHashMap JDK 8+CAS + synchronized + 细粒度锁极高非常高

6.2 JDK 7实现:分段锁

JDK 7的ConcurrentHashMap采用Segment分段锁机制:

  • 将整个Map分成多个Segment(默认16个)
  • 每个Segment独立加锁,相当于一个小型的HashMap
  • 不同Segment的写操作可以并发执行
  • 读操作几乎不加锁(volatile保证可见性)
staticfinalclassSegment<K,V>extendsReentrantLockimplementsSerializable{transientvolatileHashEntry<K,V>[] table;// ...}

6.3 JDK 8+实现:CAS + synchronized

JDK 8对ConcurrentHashMap进行了重大重构:

  1. 放弃分段锁,改用CAS + synchronized实现
  2. 与HashMap结构对齐:数组+链表+红黑树
  3. 锁粒度更细:只锁住链表或红黑树的头节点
  4. 读操作完全无锁(volatile保证可见性)
// putVal核心片段finalVputVal(K key,V value,boolean onlyIfAbsent){// ... 非空校验等for(Node<K,V>[] tab = table;;){Node<K,V> f;int n, i, fh;if(tab ==null||(n = tab.length)==0) tab =initTable();// 初始化,CAS保证线程安全elseif((f =tabAt(tab, i =(n -1)& hash))==null){// 该位置为空,CAS尝试插入if(casTabAt(tab, i,null,newNode<K,V>(hash, key, value,null)))break;}elseif((fh = f.hash)== MOVED) tab =helpTransfer(tab, f);// 帮助扩容else{V oldVal =null;synchronized(f){// 锁住链表头节点// 链表或红黑树操作}}}}

6.4 弱一致性迭代器

ConcurrentHashMap的迭代器是弱一致性的:

  • 迭代器创建后,如果Map发生修改,不会抛出ConcurrentModificationException
  • 迭代器反映的是创建时刻或之后某个时刻的数据快照
  • 迭代过程中修改Map,迭代器可能看到,也可能看不到修改结果
  • 适用于高并发场景,避免了快速失败机制带来的问题

6.5 批量操作

ConcurrentHashMap提供了强大的批量操作API:

ConcurrentHashMap<String,Integer> map =newConcurrentHashMap<>();// forEach:遍历每个元素 map.forEach(1,(k, v)->System.out.println(k +":"+ v));// search:查找第一个符合条件的元素String result = map.search(1,(k, v)-> v >100? k :null);// reduce:累加操作Integer sum = map.reduceValues(1,Integer::sum);// 用作频率统计(MultiSet)ConcurrentHashMap<String,LongAdder> freqs =newConcurrentHashMap<>(); freqs.computeIfAbsent("word", k ->newLongAdder()).increment();

parallelismThreshold参数控制并行度:小于阈值时串行执行,大于阈值时并行执行。


第七章 Map常用方法详解

7.1 基础操作方法

方法描述返回值说明
put(K key, V value)添加键值对返回该key之前的value,如果没有则返回null
get(Object key)根据key获取value存在则返回value,否则返回null
remove(Object key)删除键值对返回被删除的value
clear()清空所有键值对void
size()返回键值对数量int
isEmpty()判断是否为空boolean

7.2 查询方法

方法描述
containsKey(Object key)判断是否包含指定键
containsValue(Object value)判断是否包含指定值(HashMap中效率较低,需遍历)
getOrDefault(Object key, V defaultValue)获取值,不存在则返回默认值

7.3 遍历方法

Map的遍历方式多样,可根据场景选择:

7.3.1 entrySet遍历(最常用)
for(Map.Entry<String,Integer> entry : map.entrySet()){System.out.println(entry.getKey()+": "+ entry.getValue());}
7.3.2 keySet + get遍历
for(String key : map.keySet()){System.out.println(key +": "+ map.get(key));}// 缺点:每次get都需要二次查找,效率较低
7.3.3 values遍历(仅需值时)
for(Integer value : map.values()){System.out.println(value);}
7.3.4 Iterator遍历(支持remove)
Iterator<Map.Entry<String,Integer>> iterator = map.entrySet().iterator();while(iterator.hasNext()){Map.Entry<String,Integer> entry = iterator.next();if(entry.getValue()<0){ iterator.remove();// 安全删除}}
7.3.5 Java 8 forEach(最简洁)
map.forEach((key, value)->System.out.println(key +": "+ value));
7.3.6 Stream API遍历(支持链式操作)
map.entrySet().stream().filter(entry -> entry.getValue()>10).forEach(entry ->System.out.println(entry.getKey()));

7.4 Java 8+新增的默认方法

Java 8在Map接口中增加了多个实用默认方法,极大地简化了代码:

7.4.1 computeIfAbsent / computeIfPresent
// 如果key不存在,则通过函数计算value并放入Map map.computeIfAbsent("key", k ->newArrayList<>()).add("value");// 经典用法:实现多值MapMap<String,List<String>> multiMap =newHashMap<>(); multiMap.computeIfAbsent("group1", k ->newArrayList<>()).add("item1");// 如果key存在,则根据原值计算新值 map.computeIfPresent("key",(k, v)-> v *2);
7.4.2 merge方法
// 合并操作:如果key不存在则放入给定值,存在则通过合并函数计算新值 map.merge("key",1,Integer::sum);// 统计功能// 经典用法:单词计数String text ="apple banana apple orange apple";Map<String,Integer> wordCount =newHashMap<>();for(String word : text.split(" ")){ wordCount.merge(word,1,Integer::sum);}// 结果:{apple=3, banana=1, orange=1}
7.4.3 putIfAbsent
// 仅在key不存在时放入 map.putIfAbsent("key","value");
7.4.4 replace / replaceAll
// 替换指定key的值(仅当存在时) map.replace("key","newValue");// 对所有entry应用替换函数 map.replaceAll((k, v)-> v.toUpperCase());

7.5 Java 9的Map.of工厂方法

Java 9提供了更简洁的Map初始化方式:

// 创建不可变Map(最多支持10对键值)Map<String,Integer> map1 =Map.of("a",1,"b",2,"c",3);// 任意数量键值对Map<String,Integer> map2 =Map.ofEntries(Map.entry("a",1),Map.entry("b",2),Map.entry("c",3),Map.entry("d",4));

第八章 实现类对比与选型指南

8.1 核心特性对比

特性HashMapLinkedHashMapTreeMapHashtableConcurrentHashMap
顺序无序插入/访问顺序键排序无序无序
null键允许1个允许1个不允许不允许不允许
null值允许允许允许不允许不允许
线程安全是(全表锁)是(分段/CAS)
性能最高略低于HashMap较低(log n)读慢写快高并发下最优
底层结构数组+链表+红黑树数组+链表+红黑树+双向链表红黑树数组+链表CAS+数组+链表+红黑树
适用场景通用缓存需保持顺序需排序/范围查询遗留系统高并发共享数据

8.2 时间复杂度对比

操作HashMapLinkedHashMapTreeMapHashtableConcurrentHashMap
getO(1)O(1)O(log n)O(1)O(1)
putO(1)O(1)O(log n)O(1)O(1)
removeO(1)O(1)O(log n)O(1)O(1)
containsKeyO(1)O(1)O(log n)O(1)O(1)
containsValueO(n)O(n)O(n)O(n)O(n)

8.3 选型建议

根据不同的业务场景,选择合适的Map实现:

场景1:通用缓存,无特殊顺序要求

  • 首选:HashMap(性能最高)
  • 如果线程安全要求:ConcurrentHashMap

场景2:需要保持插入顺序

  • 首选:LinkedHashMap
  • 案例:实现FIFO队列、记录操作日志

场景3:需要按键排序或范围查询

  • 首选:TreeMap
  • 案例:排行榜、日程表、字典序输出

场景4:实现LRU缓存

  • 首选:LinkedHashMap(访问顺序模式)
  • 案例:内存缓存、最近访问记录

场景5:高并发共享数据

  • 首选:ConcurrentHashMap
  • 案例:全局配置、在线用户统计

场景6:处理配置文件

  • 首选:Properties
  • 案例:读取application.properties

8.4 性能测试数据参考

根据实际测试(百万级数据):

操作HashMapLinkedHashMapTreeMapHashtable
插入100万条1420ms1512ms3845ms797ms
读取1000万条188ms201ms892ms265ms

注:Hashtable插入快可能是由于其初始容量较小,扩容频率高导致的测试偏差,实际应用中HashMap综合性能最优。


第九章 常见陷阱与最佳实践

9.1 陷阱一:可变对象作为键

// 错误示例Map<List<String>,String> map =newHashMap<>();List<String> key =newArrayList<>(); key.add("a"); map.put(key,"value1"); key.add("b");// 键被修改,hashCode改变 map.get(key);// 返回null,再也找不到 map.containsKey(key);// false

解决方案:使用不可变对象作为键,如String、Integer,或自定义不可变类。

9.2 陷阱二:自定义类未重写hashCode和equals

classUser{String name;// 没有重写hashCode和equals}Map<User,Integer> map =newHashMap<>();User u1 =newUser("Alice");User u2 =newUser("Alice"); map.put(u1,100); map.get(u2);// 返回null,虽然内容相同

解决方案:作为键的类必须正确重写hashCode()equals()

9.3 陷阱三:并发修改导致ConcurrentModificationException

Map<String,Integer> map =newHashMap<>();// ... 填充数据for(String key : map.keySet()){if(key.startsWith("temp")){ map.remove(key);// 抛出ConcurrentModificationException}}

解决方案

// 方式1:使用Iterator的removeIterator<String> it = map.keySet().iterator();while(it.hasNext()){String key = it.next();if(key.startsWith("temp")){ it.remove();}}// 方式2:使用removeIf(Java 8+) map.keySet().removeIf(key -> key.startsWith("temp"));// 方式3:使用ConcurrentHashMap(允许并发修改)

9.4 最佳实践总结

  1. 使用泛型:指定键值类型,避免运行时类型转换异常
  2. 选择合适的实现:根据业务需求而非习惯选择
  3. 注意线程安全:多线程环境优先使用ConcurrentHashMap
  4. 避免使用Hashtable:除非维护遗留代码

优先使用Java 8+默认方法:让代码更简洁

// 老式if(!map.containsKey(key)){ map.put(key,newArrayList<>());} map.get(key).add(value);// 新式 map.computeIfAbsent(key, k ->newArrayList<>()).add(value);

预估初始容量:如果能预知数据规模,指定初始容量避免频繁扩容

Map<String,Integer> map =newHashMap<>(expectedSize *4/3+1);

结语

Java Map体系经过多年的演进,从最早的Hashtable,到JDK 1.2引入的HashMap,再到JDK 1.5的ConcurrentHashMap,以及后续的各种优化,已经形成了一套功能完备、性能卓越的数据结构家族。

理解Map的核心原理,不仅有助于写出更高效的代码,还能在遇到复杂业务场景时做出正确的技术选型。本文从源码层面剖析了各个Map实现类的底层机制,并结合实际场景给出了使用建议。在实际开发中,建议遵循"面向接口编程"的原则,根据具体需求选择最合适的Map实现,同时注意线程安全和键的不可变性等关键问题。

Map的学习是一个循序渐进的过程,掌握基础用法后,深入理解其设计思想和源码实现,才能真正做到"知其然,知其所以然"。

Read more

MCP客户端与服务端初使用——让deepseek调用查询天气的mcp来查询天气

MCP客户端与服务端初使用——让deepseek调用查询天气的mcp来查询天气

本系列主要通过调用天气的mcp server查询天气这个例子来学习什么是mcp,以及怎么设计mcp。话不多说,我们开始吧。主要参考的是B站的老哥做的一个教程,我把链接放到这里,大家如果有什么不懂的也可以去看一下。 https://www.bilibili.com/video/BV1NLXCYTEbj?spm_id_from=333.788.videopod.episodes&vd_source=32148098d54c83926572ec0bab6a3b1d https://blog.ZEEKLOG.net/fufan_LLM/article/details/146377471 最终的效果:让deepseek-v3使用天气查询的工具来查询指定地方的天气情况 技术介绍 MCP,即Model Context Protocol(模型上下文协议),是由Claude的母公司Anthropic在2024年底推出的一项创新技术协议。在它刚问世时,并未引起太多关注,反响较为平淡。然而,随着今年智能体Agent领域的迅猛发展,MCP逐渐进入大众视野并受到广泛关注。今年2月,

By Ne0inhk
可以在命令行通过大模型使用上下文协议(MCP)与外部工具交互的软件:小巧的MCPHost

可以在命令行通过大模型使用上下文协议(MCP)与外部工具交互的软件:小巧的MCPHost

小巧的MCPHost MCPHost 可以在命令行下使用,使大型语言模型(LLM)能够通过模型上下文协议(MCP)与外部工具进行交互。目前支持Claude 3.5 Sonnet和Ollama等。本次实践使用自己架设的Deepseek v3模型,跑通了Time MCP服务。  官网:GitHub - mark3labs/mcphost: A CLI host application that enables Large Language Models (LLMs) to interact with external tools through the Model Context Protocol (MCP). 下载安装 使用非常方便,直接下载解压即可使用。官网提供Windows、Linux和MacOS三个系统的压缩包: https://github.com/

By Ne0inhk
实战篇:Python开发monogod数据库mcp server看完你就会了

实战篇:Python开发monogod数据库mcp server看完你就会了

原创不易,请关注公众号:【爬虫与大模型开发】,大模型的应用开发之路,整理了大模型在现在的企业级应用的实操及大家需要注意的一些AI开发的知识点!持续输出爬虫与大模型的相关文章。 前言 目前mcp协议是给deepseek大模型插上工具链的翅膀,让大模型不仅拥有超高的推理和文本生成能力,还能具备执行大脑意识的工具能力! 如何开发一个mcp? mcp是一种协议,指的是模型上下文协议 (Model Context Protocol)。 官方结成的mcp https://github.com/modelcontextprotocol/python-sdk mcp库 pip install mcp from mcp.server.fastmcp import FastMCP 我们先来做一个简单的案例 from mcp.server.fastmcp import FastMCP import requests mcp = FastMCP("spider") @mcp.tool() def crawl(

By Ne0inhk
AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建

AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建

AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建 作者:高瑞冬 本文目录 * AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建 * 一、MCP协议简介 * 二、创建MCP工具集 * 1. 获取MCP服务地址 * 2. 在FastGPT中创建MCP工具集 * 三、测试MCP工具 * 四、AI模型调用MCP工具 * 1. 调用单个工具 * 2. 调用整个工具集 * 五、私有化部署支持 * 1. 环境准备 * 2. 修改docker-compose.yml文件 * 3. 修改FastGPT配置 * 4. 重启服务 * 六、使用MCP-Proxy集成多个MCP服务 * 1. MCP-Proxy简介 * 2. 安装MCP-Proxy * 3. 配置MCP-Proxy * 4. 将MCP-Proxy与FastGPT集成 * 5. 高级配置

By Ne0inhk