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

java中的集合框架基础-5

1.HashMap总结
 - 结构特点:Java8-中的HashMap是基于“数组+链表”的方式(链表法解决冲突),到了Java8,应该是“数组+链表/红黑树”的方式,默认阈值为树化8退化6

 - 线程安全:HashMap是不安全,JDK1.8-并发由于插入采用的是头插法可能会导致环形链,JDK1.8+插入数据采用的是尾插法可能会导致数据丢失。集合框架中有两种线程安全的实现Collections.synchronizedMap(new java.util.HashMap<>())和juc包中的ConcurrentHashMap,前者是锁整个表,后者采用乐观锁的方式
  
 - 性能特点:HashMap可以在常数时间内增加,删除,查找元素,但这也是一种平均情况,使用load factor装载因子计算阈值就是为了减少冲突带来的性能退化
  
 - 扩容方法:HashMap的桶数组一次扩展为原数组的2倍,控制扩展和移动的次数,这里需要执行rehash计算。如果容量小于64只会进行简单扩容,如果容量大于64则会进行树化改造。树化处理可以避免哈希碰撞攻击

 2.Hashtable
线程安全的,不允许null的键或值;是线程安全的但是Hashtable线程安全的策略实现代价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁。多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差


HashMap允许键和值为null,只是key只能有一个null,值允许null多个由于线程安全,则在非多线程环境下不建议使用

 Hashtable是一个散列表,它存储的内容是键值对(key-value)映射。通过"拉链法"实现的哈希表
 
 Hashtable继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

Hashtable的构造器

public Hashtable(int initialCapacity, float loadFactor) {
        //针对初始化容积和负载因子进行合法性验证	
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);
        if (initialCapacity==0)
            initialCapacity = 1;
            
        this.loadFactor = loadFactor; 
        table = new Entry<?,?>[initialCapacity];  //初始化数组,HashMap采用的是延迟初始化数组的策略
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); //获取扩容的阈值
    }
    			MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
    			
   	public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    public Hashtable() {  //无参构造器的默认值  容积为11,负载因子0.75  
        this(11, 0.75f);
    }
    	public HashMap() {
        	this.loadFactor = DEFAULT_LOAD_FACTOR; //在第一次添加put数据时,才进行数组的初始化操作,初始值为16,负载因子0.75
    	}

成员方法put

public synchronized V put(K key, V value) {  //线程安全,以当前hashtable对象充当锁
        if (value == null) {  //value值不允许为空
            throw new NullPointerException();
        }
        Entry<?,?> tab[] = table;
        int hash = key.hashCode(); //直接获取key的hash值,但是HashMap中专门定义了hash方法,用于对key的hashCode值进行扰动处理
        int index = (hash & 0x7FFFFFFF) % tab.length;   //去除hash值中的符号位求余计算获取数组的对应下标索引
        Entry<K,V> entry = (Entry<K,V>)tab[index];  //获取对应位置上的链头遍历整个链表,查找key值相同的Entry对象【首先进行hash值比较,如果相等则调用equals方法】,如果相等则后盖前
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
		//如果链表上没有相等的key,则添加entry对象到链表上
        addEntry(hash, key, value, index);   //参数1是key的hash值,并没有进行扰动处理,参数2key,参数3value,参数4是数组索引序号
        return null;
    }

Hashtable的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。此外,Hashtable中的映射不是有序的;在hashmap中允许key和value为null,只是key只能有一个null,如果出现冲突后盖前;如果使用null的key和value 则会出现一个运行时异常NullPonterException

 3.HashMap 与 HashTable区别
 1. 线程安全:HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过 synchronized修饰。(如果要保证线程安全的话就使用ConcurrentHashMap)


 2. 效率:因为线程安全的问题,HashMap要比HashTable效率高一点。另外HashTable基本被淘汰,不 要在代码中使用它

 3. 对Null key和Null value的支持:HashMap中null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。但是在HashTable中put进的键值只要有一个null,直接抛NullPointerException

 4. 初始容量大小和每次扩充容量大小的不同 :
    创建时如果不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。

    创建时如果给定了容量初始值,那么Hashtable会直接使用给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说HashMap总是使用2的幂作为哈希表的大小。

 5. 底层数据结构:JDK1.8以后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认8)时,将链表转化为红黑树,以减少搜索时间。Hashtable没有这样的机制。 

 6. 推荐使用:在Hashtable的类注释可以看到,Hashtable是保留类不建议使用,推荐在单线程环境下使用HashMap替代,如果需要多线程使用则用ConcurrentHashMap替代。

相关文章:

  • Python连接Mongodb数据库-PyMongo模块
  • 三、OO三大特性
  • SpringBoot导出Jar包并测试(使用IDEA)
  • 用Windows性能监视器测试分析网站运行状况
  • 3.【异步通信框架】RabbitMQ
  • C++学习(四八七)android studio println的输出位置
  • PCL 生成空间圆点云
  • [JS真好玩] 掘金创作者必备: 监控每天是谁取关了你?
  • Nginx服务之Rewrite
  • 【一起学Rust | 进阶篇 | reqwest库】纯 Rust 编写的 HTTP 客户端——reqwest
  • 数据库技术基础--基本概念
  • [springboot专栏]文件本地上传与提供访问服务
  • 基于AI算法的数据库异常监测系统的设计与实现
  • 猿创征文|手把手玩转docker,从入门到精通
  • 【Rust日报】2022-08-31 RustDesk 跻身 Rust 开源项目 Top 10 第九名
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • Angular 响应式表单 基础例子
  • Debian下无root权限使用Python访问Oracle
  • Git的一些常用操作
  • in typeof instanceof ===这些运算符有什么作用
  • Java IO学习笔记一
  • js对象的深浅拷贝
  • Laravel 中的一个后期静态绑定
  • Mysql5.6主从复制
  • React系列之 Redux 架构模式
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • 从tcpdump抓包看TCP/IP协议
  • 对象引论
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 工程优化暨babel升级小记
  • 前端攻城师
  • 如何在 Tornado 中实现 Middleware
  • 最近的计划
  • Semaphore
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (Matlab)使用竞争神经网络实现数据聚类
  • (SpringBoot)第七章:SpringBoot日志文件
  • (编译到47%失败)to be deleted
  • (二)WCF的Binding模型
  • (二)构建dubbo分布式平台-平台功能导图
  • (分布式缓存)Redis分片集群
  • (附源码)计算机毕业设计ssm电影分享网站
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (顺序)容器的好伴侣 --- 容器适配器
  • (转)socket Aio demo
  • *Django中的Ajax 纯js的书写样式1
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET Core 成都线下面基会拉开序幕
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .php结尾的域名,【php】php正则截取url中域名后的内容