一、map的通用方法
- get(),put(),size(),remove(),containsKey()等
测试代码如下:
//创建10个HashMap,每个HashMap包含10万条记录
//传递不同的构造方法的参数,比较速度
//实验参数:构造方法(16.,075f)和(16384,0.75f)
public class MapBaseStudy {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("x","123");
map.put("y","456");
//遍历map中的元素
map.forEach((key,value) -> {
System.out.println("key="+key+"=value="+value);
});
//获取默认值,如果有输出对应的value;如果没有输出默认值
System.out.println(map.getOrDefault("x1","zzz"));
//判断map是否为空
System.out.println("map.isEmpty()====="+map.isEmpty());
//删除map中的元素
map.remove("x");
System.out.println(map);
//替换map中某个key对应的value,key匹配,即替换
map.replace("y","wwwwwwwwww");
System.out.println(map);
//验证key和oldevalue匹配才替换
map.replace("y","www","66");
System.out.println(map);
//使用put方法,也能达到替换值的目的
map.put("y","zzzzz");
System.out.println(map);
//如果没有相同key的元素,则存入,并返回空。如果有则不存入,返回原value值
map.putIfAbsent("y","rrr");
//判断map中是否包含某元素
System.out.println(map.containsKey("y"));
//清空map
map.clear();
System.out.println(map);
}
}
二、map的常用三种构造
- 1、无参数构造:Map<String,String> map = new HashMap<String,String>();,默认初始化长度为 16
- 2、带一个参数,指明初始化大小:Map<String,String> map = new HashMap<String,String>(10);
- 3、带两个参数,指明初始化大小,负载因子(默认0.75,即map中保存的内容超过其初始化大小的75%时会扩容,优化时使用);Map<String,String> map = new HashMap<String,String>(10,0.75f);
三、hashMap的Entry结构
static class Entry<K,V>{ //
final K key, // put方法中的key
V value, // put方法中的value
Entry<K,V> next; //map中put进内容,根据位运算计算位置时,有时会相同,相同的值会保存在next中。如果没有相同的,则next为空
final int hash; //key对应的hash码,用于确定值的位置
}
ps:map的输出位置不是put顺序的位置
四、遍历map
- 1、使用map.keySet()(不推荐)
for(String key:map.keySet()){
System.out.println(key+"=="+map.get(key));
}
- 2、使用map.values()(推荐)
for(String v:map.values()){
System.out.println("value=="+v);
}
- 3、使用entrySet遍历
for(Map.Entry<String,String> entry : map.entrySet()){
System.out.println(entry.getKey+"==="+entry.getValue());
}
- 4、使用Iterator遍历(推荐)
Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry<String,String> entry = iter.next();
System.out.println(entry.getKey+"==="+entry.getValue());
}
- 5、使用map.forEach((key,value)) 遍历
//遍历map中的元素
map.forEach((key,value) -> {
System.out.println("key="+key+"=value="+value);
});
ps:测试map遍历方法的性能,需要尽可能的让key接近真实环境
//map性能测试
//不使用System.out.println,不方便查看时间
//统一定义给value赋值
public class MapStudy {
public static void main(String[] args) {
Map<String,Integer> map = initMap();
showMapKeySet(map);
showMapEntrySet(map);
showMapEntryIterator(map);
showMapValues(map);
}
public static void showMapKeySet(Map<String,Integer> map){
Long start = System.currentTimeMillis();
Integer value;
for(String key:map.keySet()){
// System.out.println(key+"=="+map.get(key));
value = map.get(key);
}
Long end = System.currentTimeMillis();
System.out.println("showMapKeySet==消耗="+ (end - start));
}
public static void showMapEntrySet(Map<String,Integer> map){
Long start = System.currentTimeMillis();
Integer value;
for(Map.Entry<String,Integer> entry : map.entrySet()){
// System.out.println(entry.getKey()+"==="+entry.getValue());
value = entry.getValue();
}
Long end = System.currentTimeMillis();
System.out.println("showMapEntrySet==消耗="+ (end - start));
}
public static void showMapEntryIterator(Map<String,Integer> map){
Long start = System.currentTimeMillis();
Iterator<Map.Entry<String,Integer>> iter = map.entrySet().iterator();
Integer value;
while(iter.hasNext()){
Map.Entry<String,Integer> entry = iter.next();
// System.out.println(entry.getKey()+"==="+entry.getValue());
value = entry.getValue();
}
Long end = System.currentTimeMillis();
System.out.println("showMapEntryIterator==消耗="+ (end - start));
}
public static void showMapValues(Map<String,Integer> map){
Long start = System.currentTimeMillis();
Integer value;
for(Integer v:map.values()){
// System.out.println("value=="+v);
value = v;
}
Long end = System.currentTimeMillis();
System.out.println("showMapValues==消耗="+ (end - start));
}
public static Map<String,Integer> initMap(){
Map<String,Integer> map = new HashMap<String,Integer>();
String str[] = new String[]{"a","b","c","d","e","f","g","w","x","y","z"};
String key;
Integer value;
for(int i=0;i<=1000000;i++){
int m = (int)(Math.random() + 10);
key = String.valueOf(str[m]+"x"+i);
value = i;
map.put(key,value);
}
return map;
}
}
//ps 第一次执行时,java虚拟机需要分配内存,消耗时间较多
五、HashMap的底层原理
- 1、HashMap是无序的:遍历输出的顺序与put进去的顺序无关,也不是按照自然数升降序,也不是随机的。
- 2、模拟map的保存方法。用数组表示初始化位置,初始化长度为16,在底层以位运算求余数,此处用%号代替。
-
2.1 Entry<K,V> next; //map中put进内容,根据位运算计算位置时,有时会相同,相同的值会保存在next中。如果没有相同的,则next为空 .如下图中的 40
image.png - 3、如果key是字符串
- 3.1 用hashCode()方法将key转换为hash码后并进行优化得到优化后的hash码。例如:‘yuwen’字符串优化后的hash码是115347492。使用方法:final int hash(Object k)
- 3.2 对优化后的hash码进行取址,确定其在HashMap中的位置。例如:115347492在长度是16的HashMap中,取址坐标为4。使用方法:staic int indexFor(int h,int length)
- 3.3 默认长度为16,默认负载因子是0.75,默认扩容长度是 *2。即map中保存的内容长度到12时,就会扩容到32,此时map中的内容会要根据新的长度32来重新计算位置。
- 3.4 计算是否扩容的长度时,相同的节点算1次。即上面的120和40的长度算1
六、HashMap构造方法优化
- 1、Map<String,String> map = new HashMap<String,String>(3);
如上,并不是声明了一个初始化长度为3的map。构造函数会默认优化 取大于3的最小的2的n次方的数字,作为长度,即实际创建长度为4 - 2、尽可能的避免扩容,因为每次扩容都需要重新计算map中元素的位置
//创建10个HashMap,每个HashMap包含10万条记录
//传递不同的构造方法的参数,比较速度
//实验参数:构造方法(16.,075f)和(16384,0.75f)
//ps 第一次执行时,java虚拟机需要分配内存,消耗时间较多
public class MapConStudy {
public static void main(String[] args) {
Long sum = 0L;
for(int i=0;i<10;i++){
sum+= initMap(16,0.75f);
}
System.out.println("=====" + sum/10);
}
public static Long initMap(int initialCapacity,float loadFactor){
String key,value;
Map<String,String> map = new HashMap<String,String>(initialCapacity,loadFactor);
String str[] = new String[]{"a","b","c","d","e","f","g","w","x","y","z"};
Long start = System.currentTimeMillis();
for(int i=0;i<=1000000;i++){
int m = (int)(Math.random() + 10);
key = str[m]+"x"+i;
value = "abc";
map.put(key,value);
}
Long end = System.currentTimeMillis();
Long time = end - start;
System.out.println("time==消耗="+ time);
return time;
}
}
七、LinkedHashMap
- 1、性能比较:put方法,linkedMap耗时较长;遍历时,HashMap耗时较长;数据量越大,效果越明显。
//采用不带参的空构造方法
//分别给HashMap和LinkedHashMap存储100万数据,并循环遍历,观察耗时
public class LinkedHashMapStudy {
public static void main(String[] args) {
int count = 1000000;
Map<String,String> hashMap = new HashMap<String,String>();
Map<String,String> linkedMap = new LinkedHashMap<String,String>();
Long start = System.currentTimeMillis();
for(int i=0;i<count;i++){
hashMap.put(String.valueOf(i),"abc");
}
Long end = System.currentTimeMillis();
System.out.println("hashMap=put=消耗="+ (end - start));
Long start2 = System.currentTimeMillis();
for(String value :hashMap.values()){
}
Long end2 = System.currentTimeMillis();
System.out.println("hashMap=遍历value=消耗="+ (end2 - start2));
Long start1 = System.currentTimeMillis();
for(int i=0;i<count;i++){
linkedMap.put(String.valueOf(i),"abc");
}
Long end1 = System.currentTimeMillis();
System.out.println("linkedMap=put=消耗="+ (end1 - start1));
Long start3 = System.currentTimeMillis();
for(String value :linkedMap.values()){
}
Long end3 = System.currentTimeMillis();
System.out.println("linkedMap=遍历value=消耗="+ (end3- start3));
}
}
- 2、特有方法
- 2.1 两种输出顺序:1、录入顺序 2、使用顺序
- 2.2 利用LinkedHashMap实现LRU,参考代码:
//
public class LinkedHashMapBaseStudy {
public static void main(String[] args) {
Map<String,String> lruMap = new LRUMap<String,String>(3,0.75f,true);
lruMap.put("a","a");
lruMap.put("b","b");
lruMap.put("c","c");
lruMap.get("a");
lruMap.put("d","d");
lruMap.put("e","e");
System.out.println(lruMap);
//默认顺序输出
Map<String,String> map = new LinkedHashMap<String,String>();
map.put("a","a");
map.put("b","b");
map.put("c","c");
map.put("d","d");
map.put("e","e");
System.out.println(map);
//按照使用顺序输出,最后使用的放在最后输出
Map<String,String> mapOrder = new LinkedHashMap<String,String>(16,0.75f,true);
mapOrder.put("a","a");
mapOrder.put("b","b");
mapOrder.put("c","c");
mapOrder.put("d","d");
mapOrder.put("e","e");
mapOrder.get("d");
mapOrder.get("c");
System.out.println(mapOrder);
}
public static class LRUMap<K,V> extends LinkedHashMap<K,V> {
private static final long serialVersionUID = 9092683527301131632L;
private int maxSize; //map中保存的最大数量
public LRUMap(int maxSize, float v, boolean b){
super(16,0.75f,true);//
this.maxSize = maxSize;
}
//返回true时,删除掉最老的内容
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest){
return size() > this.maxSize;
}
}
}
七、TreeMap
- 1、按照自然数进行排序
public class TreeMapStudy {
public static void main(String[] args) {
Map<String,String> treeMap = new TreeMap<String,String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
treeMap.put("aa","a");
treeMap.put("tx","b");
treeMap.put("ac","c");
System.out.println(treeMap);
}
}
网友评论