在用eclipse维护一个老项目的过程中,debug代码的时候发现一个LinkedCaseInsensitiveMap类型的map,get的时候不区分key值的大小写,但是在put的时候会区分大小写。如:
LinkedCaseInsensitiveMap<String> map = new LinkedCaseInsensitiveMap<String>();
//在eclipse中debug时,在Variables窗口点击该map变量会发现显示为{Aa:1,aa:2}
map.put("Aa","1");
map.put("aa","2");
//get时均会显示2
map.get("Aa");//显示:2
map.get("aa");//显示:2
后查看源码才得知,由于老项目中引用的是org.springframework.core-3.0.2.RELEASE.jar
在该版本jar包中LinkedCaseInsensitiveMap.java的属性及其get和put方法内容如下:
package org.springframework.util;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
public class LinkedCaseInsensitiveMap<V> extends LinkedHashMap<String, V> {
private final Map<String, String> caseInsensitiveKeys;
private final Locale locale;
//调用put方法时,
@Override
public V put(String key, V value) {
//1.将传入的key,用convertKey(key)方法将字母转换成小写返回lowerCaseKey
//(为便于理解,转换后的key命名为lowerCaseKey),
//再将<lowerCaseKey, key>该键值对存入caseInsensitiveKeys
this.caseInsensitiveKeys.put(convertKey(key), key);
//2.将<传入的key,传入的value>存入LinkedHashMap中(该类继承了LinkedHashMap)
return super.put(key, value);
}
//调用get方法时,
@Override
public V get(Object key) {
if (key instanceof String) {
//1.convertKey((String) key):将传入的key中字母转换成小写lowerCaseKey
//2.this.caseInsensitiveKeys.get(lowerCaseKey):
//获取 lowerCaseKey在caseInsensitiveKeys对应的值key(该key其实就是put时传入的原key)
//3.super.get(key):获取key在LinkedHashMap对应的value(该value才是put时传入的value)
return super.get(this.caseInsensitiveKeys.get(convertKey((String) key)));
}
else {
return null;
}
}
//将key中的字母根据locale的规则转换成小写
protected String convertKey(String key) {
return key.toLowerCase(this.locale);
}
}
(注:v3.0.2.RELEASE 中完整的LinkedCaseInsensitiveMap源代码,通过以下地址可查看spring-framework/LinkedCaseInsensitiveMap.java at v3.0.2.RELEASE · spring-projects/spring-framework · GitHub)
看了以上源代码再分析一下,我开头举得例子:
LinkedCaseInsensitiveMap<String> map = new LinkedCaseInsensitiveMap<String>();
//map.put("Aa","1")执行完后,map中属性caseInsensitiveKeys变为:{"aa":"Aa"},其父类LinkedHashMap变为:{"Aa":"1"}
map.put("Aa","1");
//map.put("aa","2")执行完后,map中属性caseInsensitiveKeys变为:{"aa":"aa"}(原来的值"Aa"被覆盖),其父类LinkedHashMap变为:{"Aa":"1","aa":"2"}
map.put("aa","2");
//map.get("Aa")会先caseInsensitiveKeys.get("aa")得到aa,再根据aa获取LinkedHashMap的值,即LinkedHashMap.get("aa")得到值2.
//map.get("aa")也是相同过程,因为根据源代码convertKey("Aa")和convertKey("aa")得到的值是一致的
map.get("Aa");//显示:2
map.get("aa");//显示:2
综上,LinkedCaseInsensitiveMap类继承了LinkedHashMap,又定义了Map<String, String> caseInsensitiveKeys属性。caseInsensitiveKeys用来存<字母转换成小写的key,key>,真正put时的<key,value>存在其父类LinkedHashMap中。由于字符串”aa“、”Aa“转换成小写都是"aa",所以在存入caseInsensitiveKeys时后一个put的会覆盖前一个的key(即对应上面例子<"aa","aa">会覆盖<"aa","Aa">),而由于存入LinkedHashMap时,"aa"、”Aa“不同,所以都会被存入(先put的一个所对应的value,通过map.get不管参数key大写还是小写永远获取不到,虽然在LinkedHashMap中存在)
补充:在github上springframework.core-3.1.3.RELEASE版本中,LinkedCaseInsensitiveMap源代码put函数中,若put时key转换后的小写相同,caseInsensitiveKeys后put的覆盖前put的key,同时会移除LinkedHashMap中前put的key对应的value
@Override
public V put(String key, V value) {
String oldKey = this.caseInsensitiveKeys.put(convertKey(key), key);
if (oldKey != null && !oldKey.equals(key)) {
super.remove(oldKey);
}
return super.put(key, value);
}
(注:v3.1.3.RELEASE中完整的LinkedCaseInsensitiveMap源代码,通过以下地址可查看https://github.com/spring-projects/spring-framework/blob/v3.1.3.RELEASE/org.springframework.core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java)
如有错误,望指正!