美文网首页
java map从0到略懂

java map从0到略懂

作者: 赵荣晖 | 来源:发表于2020-11-06 11:35 被阅读0次

一、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);
    }
}

相关文章

  • java map从0到略懂

    一、map的通用方法 get(),put(),size(),remove(),containsKey()等测试代码...

  • java遍历Map的几种方法

    java中的map遍历有多种方法,从最早的Iterator,到java5支持的foreach,再到java8 La...

  • 谈谈Java中遍历Map的几种方法

    java中的map遍历有多种方法,从最早的Iterator,到java5支持的foreach,再到java8 La...

  • JavaScript map方法的运用

    array 对象的map()方法 currentValue 当前函数值 过程:对array进行遍历,从0到arra...

  • 从0到0,从0到1。

    昨天和一客户交流,听到这么一句话,我现在的阶段勉强算0到0的阶段,到那个1的阶段还没有看到,或者说并不知道那个1在...

  • 0基础学Java?我从彷徨到曙光的心路历程

    0基础学Java?我从彷徨到曙光的心路历程 我是从0基础开始学Java的,在来到尚学堂之前,我对java一无所知。...

  • java day 14

    Map java Map及Map.Entry详解Map是java中的接口,Map.Entry是Map的一个内部接口...

  • 从0到0

    我们降临世间 感受人情冷暖 尝遍酸甜苦辣 走过荆棘坎坷 收获功成名就 最后带着所有乱世繁华 走进黄土白骨

  • 从0到0

    隔壁病床的老爷爷今年79了,神志老是半清不醒的。晚上陪床的阿姨考考他,问他自己儿子,女儿,孙子外孙的名字,老人答得...

  • Java中如何遍历Map对象的4种方法

    在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍...

网友评论

      本文标题:java map从0到略懂

      本文链接:https://www.haomeiwen.com/subject/grqtbktx.html