当前位置: 首页 > news >正文

java集合专题Map接口及HashMap/Hashtable/Properties使用方法底层结构及源码分析

目录点击跳转

  • Map接口
    • Map接口常用方法
    • **Map遍历方式**
    • 练习题
  • HashMap
    • 底层结构源码分析
  • Hashtable
    • 底层结构源码分析
  • TreeMap
    • 源码分析
  • Properties

Map接口

Map接口我们主要学习其下的HashMap/Hashtable/TreeMap/Properties4个主要实现类

在这里插入图片描述
Map不同于Colletion接口下的List和Set! Map是双列集合存放的是键值对的形式!
Key-Value的形式进行存储

  • 无序,无法保证插入顺序和取出顺序一致
  • key可以为null且唯一,value可以为null
  • Map中的key-value是任意引用数据类型,会封装在HashMap&Node对象中!

Map接口常用方法

在这里插入图片描述

public class Map_ {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        //添加元素!
        map.put("李白",19);
        //通过key获取value!
        System.out.println("李白:"+map.get("李白"));
        //删除元素!
        map.remove("李白");
        System.out.println("size:"+map.size());
        //替换,如果需要替换的key不存在,不会添加该元素(和put的区别)
        map.replace("李白",100);
        System.out.println(map);
        Map<String,Integer> map1 = new HashMap<>();
        map.put("李白",190);
        map1.put("曹操",18);
        map1.put("关羽",31);
        map1.put("张飞",12);
        //添加map1到map中!
        map.putAll(map1);
        System.out.println(map);
    }
}

在这里插入图片描述

Map遍历方式

使用下面的4种方法从而实现map遍历

  • containsKey:查找key是否存在
  • KeySet:获取map所有key值
  • entrySet:获取所有的key-value
  • values:获取所有的value
    在这里插入图片描述
    注意:这里map下的键值对是通过Node进行存储的!
    这里的keySet或者 Map.Entry只是保存的索引值,只是为了遍历方便!
public class MapFor {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            map.put(i+"",i*i);
        }
        System.out.println("===1.keySet获取key后通过get获取value===");
        Set<String> keyset = map.keySet();
        System.out.println("1.1迭代器遍历");
        Iterator<String> iterator = keyset.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            System.out.print(next+"->"+map.get(next)+" ");
        }
        System.out.println("\n===1.1增强for遍历===");
        for ( String x:keyset) {
            System.out.print(x+"->"+map.get(x)+" ");
        }
        //我们知道set没有get方法所以不能进行普通for遍历!
        System.out.println("\n===2.通过EntrySet获取到key-value===");
        //这里注意:因为entrySet的类型Map.Entry:map下的Entry存放了Node的key-value引用
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        System.out.println("===2.1迭代器iterator遍历===");
        Iterator<Map.Entry<String, Integer>> iterator1 = entries.iterator();
        while (iterator1.hasNext()){
            Map.Entry<String,Integer> next = iterator1.next();
            //Map.Entry类提供了 getKey 和 getValue 方法!
            System.out.print(next.getKey()+"->"+next.getValue()+" ");
        }
        System.out.println("\n===2.2增强for遍历===");
        for (Map.Entry<String,Integer> x : entries) {
            System.out.print(x.getKey()+"->"+x.getValue()+" ");
        }
        //这里依然是得到的set集合,而set没有get方法,不能普通for遍历
        System.out.println("\n===3.values方法获取所有的value值===");
        //注意这里values方法返回的是Collection接口实现类
        Collection<Integer> values = map.values();
        System.out.println("===3.1迭代器遍历===");
        Iterator<Integer> iterator2 = values.iterator();
        while (iterator2.hasNext()){
            Integer next = iterator2.next();
            System.out.print(next+" ");
        }
        System.out.println("\n===3.2增强for遍历===");
        for (Integer x :values) {
            System.out.print(x+" ");
        }

    }
}

在这里插入图片描述

练习题

使用HashMap添加3个员工对象
要求key:员工id 值: 员工对象
遍历结果显示工资> 18000的员工
员工类:姓名,工资,员工id

public class MapExercies {
    //员工类!
    static class Emp{
        private int id;
        private String name;
        private int sal;
        public Emp(int id, String name, int sal) {
            this.id = id;
            this.name = name;
            this.sal = sal;
        }

        @Override
        public String toString() {
            return "Emp{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", sal=" + sal +
                    '}';
        }
    }

    public static void main(String[] args) {
        Map<Integer,Emp> map = new HashMap<>();
        map.put(1,new Emp(1,"刘备",16000));
        map.put(4,new Emp(4,"曹操",19000));
        map.put(2,new Emp(2,"吕布",1000));
        //entrySet方法先获取到 key-value
        Set<Map.Entry<Integer,Emp>> entries = map.entrySet();
        System.out.println("=迭代器遍历=");
        Iterator<Map.Entry<Integer, Emp>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<Integer,Emp> next = iterator.next();
            if(next.getValue().sal>18000){
                System.out.println(next.getKey()+"->"+next.getValue());
            }
        }
        Set<Integer> keyset = map.keySet();
        System.out.println("=keySet增强for遍历=");
        for (Integer key : keyset) {
            if(map.get(key).sal>18000){
                System.out.println(key+"->"+map.get(key));
            }
        }

    }
}

在这里插入图片描述

HashMap

  • HashMap是Map接口中使用频率最高的类
  • HashMap是以key-value的形式存储(HashMap$Node类型)
  • Key值不能重复,value可以重复如果key相同,key不变,value会覆盖,允许key和value为空
  • 和HashSet一样不能保证映射顺序,hash表的方式存储(jdk8底层是数组+链表+红黑树)
  • HashSet没有实现线程同步,线程不安全

底层结构源码分析

在这里插入图片描述

重点:

扩容机制:和HashSet相同
1.HashMap底层维护了Node类型的table,默认为空
2.创建对象初始化,只是将装载因子(loadfacter)赋值为0.75
3.判断2个key相同的方式:先hash处理(调用该key的hashCode方法获取h^h>>>16),然后进行索引映射(i=(len-1)%hash),如果索引位置为空直接添加,如果非空判断引用是否相同 | | equals是否相同如果满足其一就说明key相同进行value覆盖,否则链表尾插
3.当添加第一个对象时,第一次扩容为16(threshold阀值为16*0.75)
4.以后进行扩容,超过阀值就进行2倍扩容,然后更新阀值
5.在jdk8当一条链表的长度超过8就会进行树化判断,如果当前容量(数组的长度)大于等于64就会将该链红黑树树化如果未达到数组长度未到达64就会进行2倍扩容

这里的源码分析和HashSet一样就不在进行分析了,下次一定(等忘了再来!)

Hashtable

在这里插入图片描述

  • Hashtable类和HashMap底层相同不过是数组+链表不会树化
  • Hashtable线程安全,方法添加了synchronized关键字
  • Hashtable中key和value都不能为空!
  • 初始化容量即为11,然后loadfactor=0.75
  • 扩容方式,超过阀值就就进行扩容,2倍+1

底层结构源码分析

在这里插入图片描述

TreeMap

  • TreeMap底层是一颗红黑树
  • TreeMap可以传入比较器进行排序
  • TreeMap无序集合(插入和读取不一致)
  • TreeMap可以为null

在这里插入图片描述可以看到TreeMap继承了AbstractMap类
在这里插入图片描述
AbstractMap类继承了Map接口这样就减少了TreeMap类实现Map接口的复杂性,直接继承AbstactMap类即可! 这里还继承了NavigableMap接口
在这里插入图片描述
而NavigableMap实现了SorteMap接口!
SortMap接口有comparator方法,所以TreeMap类可以传入比较器进排序
在这里插入图片描述

public class TreeMap_ {
    //通过年龄进行排序
   static class Emp{
        private String name;

        @Override
        public String toString() {
            return "Emp{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }

        public Emp(String name, int age) {
            this.name = name;
            this.age = age;
        }

        private int age;
    }
    public static void main(String[] args) {
        TreeMap<Emp,String> treeMap = new TreeMap<>(new Comparator<Emp>() {
            @Override//年龄升序排列
            public int compare(Emp o1, Emp o2) {
                return o1.age-o2.age;
            }
        });
        treeMap.put(new Emp("刘备",123),"1");
        treeMap.put(new Emp("张飞",888),"1");
        treeMap.put(new Emp("李白",5),"1");
        System.out.println(treeMap);
    }

}

在这里插入图片描述

源码分析

在这里插入图片描述
有点复杂红黑树的插入,所以咱们后面再回来干他!

Properties

Properties类继承了Hashtable!
他主要特点就是为了读取.properties等资源配置配置文件用于IO操作的一个类!

  • key-value都为字符串
  • 虽然Hashtable的那些方法都能用,我们一般都是用该类下特有的方法

在这里插入图片描述

public class Properties_ {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        //保存数据
        properties.setProperty("刘备","14");
        properties.setProperty("李白","24");
        properties.setProperty("张飞","13");
        //获取数据
        System.out.println("李白:"+properties.getProperty("李白"));
        //将数据保存到配置文件!
        OutputStream file = new FileOutputStream("db.properties",true);
        properties.store(file,"注解");
        System.out.println(properties);
        //读数据!
        Properties prop = new Properties();
        prop.load(new FileReader("db.properties"));
        System.out.println(prop);
    }
}

在这里插入图片描述

在这里插入图片描述

相关文章:

  • Vue(六)——vuex
  • JavaScript 学习-47.export 和 import 的使用
  • Kafka 生产者
  • Spring核心IOC的核心类解析
  • 【数据挖掘】恒生金融有限公司2023届秋招数据ETL工程师笔试题解析
  • 软件测试分类
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • var、let、const的区别
  • 机器学习(二十九):LightGBM 模型
  • node.js 使用教程-2.Gulp 打包构建入门与使用
  • UMLChina建模竞赛第3赛季第12轮:歌曲知识
  • springboot二手交易平台毕业设计源码290915
  • MySQL-触发器
  • 使用 Jest 对 Vuex 模块进行单元测试
  • 常用的芯片封装与PCB封装总结
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Asm.js的简单介绍
  • ComponentOne 2017 V2版本正式发布
  • eclipse的离线汉化
  • E-HPC支持多队列管理和自动伸缩
  • Java 多线程编程之:notify 和 wait 用法
  • Java小白进阶笔记(3)-初级面向对象
  • Nacos系列:Nacos的Java SDK使用
  • nginx 配置多 域名 + 多 https
  • 对象引论
  • 复习Javascript专题(四):js中的深浅拷贝
  • 官方解决所有 npm 全局安装权限问题
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 世界上最简单的无等待算法(getAndIncrement)
  • 手写一个CommonJS打包工具(一)
  • 首页查询功能的一次实现过程
  • 移动端解决方案学习记录
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (未解决)macOS matplotlib 中文是方框
  • (原)Matlab的svmtrain和svmclassify
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .Net core 6.0 升8.0
  • .Net6 Api Swagger配置
  • .NET大文件上传知识整理
  • .NET企业级应用架构设计系列之应用服务器
  • .Net中间语言BeforeFieldInit
  • .ui文件相关
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [ 隧道技术 ] 反弹shell的集中常见方式(四)python反弹shell
  • [android] 看博客学习hashCode()和equals()
  • [Docker]十.Docker Swarm讲解
  • [ERROR] 不再支持目标选项 5。请使用 7 或更高版本
  • [flask]http请求//获取请求体数据
  • [go] 策略模式
  • [Hibernate] - Fetching strategies
  • [IE9] IE9 Beta崩溃问题解决方案