1.hash / hashtable(linux kernel 哈希表)
2.HashSet 源码分析及线程安全问题
3.Hermes源码分析(二)——解析字节码
4.password_hash用法
5.HashMap实现原理一步一步分析(1-put方法源码整体过程)
hash / hashtable(linux kernel 哈希表)
哈希表,源码或称为散列表,图解是源码一种高效的数据结构,因其插入和查找速度的图解优势而备受关注。然而,源码其空间利用率并不固定,图解进度管理APP源码需要权衡。源码让我们通过实例来深入理解它的图解作用和工作原理。
想象一个场景:我们需要高效地存储和访问大量数据。源码首先,图解常规的源码数组方法,如普通数组和有序数组,图解虽然插入简单,源码但查找效率低,图解尤其是源码在数据量较大时。例如,查找可能需要对数千个元素进行比较。有序数组通过牺牲增删效率来提升查询,但数组空间固定且可能浪费大量资源。
链表提供了更灵活的增删操作,但随机访问困难,适合数据频繁变动的情况。红黑树在查询和增删效率上表现优秀,netapp源码但此处暂不讨论。庞大的数组虽然理论上能快速查找,但实际操作中难以实现,因为它需要预先预估并准备极大数据空间。
这时,哈希表登场了。它利用哈希函数将数据映射到一个较小的数组中,即使存在冲突(不同数据映射到同一地址),通过链表解决,仍然能显著提升查找效率。例如,即使身份证号的哈希结果可能有重复,但实际冲突相对较少,通过链表链接,平均查找次数大大减少。
使用哈希表包括简单的步骤:包含头文件,声明和初始化哈希表,添加节点,以及通过哈希键查找节点。在实际源码中,如Linux kernel的hash.h和hashtable.h文件,哈希表的龙山源码初始化和操作都是基于这些步骤进行的。
总结来说,哈希表在大数据场景中通过计算直接定位数据,显著提高效率,尤其是在数据量增大时。如果你对Linux kernel的哈希表实现感兴趣,可以关注我的专栏RTFSC,深入探讨更多源码细节。
HashSet 源码分析及线程安全问题
HashSet,作为集合框架中的重要成员,其底层采用 HashMap 进行数据存储,简化了集合操作的复杂性。深入理解 HashMap,将有助于我们洞察 HashSet 的源码精髓。
一、HashSet 定义详解
1.1 构造函数
HashSet 提供了多种构造函数,允许用户根据需求灵活创建实例。例如,使用 HashSet() 创建一个空 HashSet,或者通过 Collection 参数构造,实现与现有集合的合并。
1.2 属性定义
HashSet 主要属性包括容量(容量决定 HashMap 的大小)和负载因子(控制容量的扩展阈值),确保其高效存储和检索数据。standup 源码
二、操作函数
2.1 add() - 向集合中添加元素,若元素已存在则不添加。
2.2 size() - 返回集合中元素的数量。
2.3 isEmpty() - 判断集合是否为空。
2.4 contains() - 检查集合中是否包含指定元素。
2.5 remove() - 删除集合中的指定元素。
2.6 clear() - 清空集合,使其变为空。
2.7 iterator() - 返回一个可迭代对象,用于遍历集合中的元素。
2.8 spliterator() - 返回一个 Spliterator,用于更高效地遍历集合。
三、HashSet 线程安全吗?
3.1 线程安全解决
HashSet 不是线程安全的,它不保证在多线程环境下的并发访问。为了确保线程安全,用户需要采用同步机制,如使用 Collections.synchronizedSet() 方法将 HashSet 转换为同步集合。同时,利用并发集合如 CopyOnWriteArrayList 和 ConcurrentHashMap 等,可以实现更高效、intoken源码安全的并发操作。
Hermes源码分析(二)——解析字节码
前面一节 讲到字节码序列化为二进制是有固定的格式的,这里我们分析一下源码里面是怎么处理的这里可以看到首先写入的是魔数,他的值为
对应的二进制见下图,注意是小端字节序
第二项是字节码的版本,笔者的版本是,也即 上图中的4a
第三项是源码的hash,这里采用的是SHA1算法,生成的哈希值是位,因此占用了个字节
第四项是文件长度,这个字段是位的,也就是下图中的为0aa,转换成十进制就是,实际文件大小也是这么多
后面的字段类似,就不一一分析了,头部所有字段的类型都可以在BytecodeFileHeader.h中看到,Hermes按照既定的内存布局把字段写入后再序列化,就得到了我们看到的字节码文件。
这里写入的数据很多,以函数头的写入为例,我们调用了visitFunctionHeader方法,并通过byteCodeModule拿到函数的签名,将其写入函数表(存疑,在实际的文件中并没有看到这一部分)。注意这些数据必须按顺序写入,因为读出的时候也是按对应顺序来的。
我们知道react-native 在加载字节码的时候需要调用hermes的prepareJavaScript方法, 那这个方法做了些什么事呢?
这里做了两件事情:
1. 判断是否是字节码,如果是则调用createBCProviderFromBuffer,否则调用createBCProviderFromSrc,我们这里只关注createBCProviderFromBuffer
2.通过BCProviderFromBuffer的构造方法得到文件头和函数头的信息(populateFromBuffer方法),下面是这个方法的实现。
BytecodeFileFields的populateFromBuffer方法也是一个模版方法,注意这里调用populateFromBuffer方法的是一个 ConstBytecodeFileFields对象,他代表的是不可变的字节码字段。
细心的读者会发现这里也有visitFunctionHeaders方法, 这里主要为了复用visitBytecodeSegmentsInOrder的逻辑,把populator当作一个visitor来按顺序读取buffer的内容,并提前加载到BytecodeFileFields里面,以减少后面执行字节码时解析的时间。
Hermes引擎在读取了字节码之后会通过解析BytecodeFileHeader这个结构体中的字段来获取一些关键信息,例如bundle是否是字节码格式,是否包含了函数,字节码的版本是否匹配等。注意这里我们只是解析了头部,没有解析整个字节码,后面执行字节码时才会解析剩余的部分。
evaluatePreparedJavaScript这个方法,主要是调用了HermesRuntime的 runBytecode方法,这里hermesPrep时上一步解析头部时获取的BCProviderFromBuffer实例。
runBytecode这个方法比较长,主要做了几件事情:
这里说明一下,Domain是用于垃圾回收的运行时模块的代理, Domain被创建时是空的,并跟随着运行时模块进行传播, 在运行时模块的整个生命周期内都一直存在。在某个Domain下创建的所有函数都会保持着对这个Domain的强引用。当Domain被回收的时候,这个Domain下的所有函数都不能使用。
未完待续。。。
password_hash用法
在探索项目源码的过程中,我偶然发现了一个不常使用的PHP函数——password_hash。过去,我对于密码加密的理解仅仅停留在简单的MD5或sha1方式,加上salt。然而,通过查阅相关资料,我意识到现代加密方式的进阶。
密码哈希函数password_hash用于将原始密码安全地转换为哈希值。这个过程涉及到使用一个内置的加密算法,如bcrypt、scrypt或argon2,而不是简单的哈希函数。这提供了更好的安全性,因为即使哈希值泄露,原始密码也难以被复原。
以下是一个示例,展示如何使用password_hash生成哈希值:
经过上述操作,你将得到一个类似于"2a$$..."的哈希值。这个哈希值包含了加密算法的名称,迭代次数以及其他元数据。
紧接着,我们需要使用password_verify函数来验证用户输入的密码是否与数据库中存储的哈希值匹配。该函数接受原始密码和哈希值作为参数,并返回一个布尔值,指示密码是否正确。
以下示例展示了如何使用password_verify进行密码验证:
执行此验证后,如果输入的密码正确,将会输出“验证通过”。
通过深入了解并应用password_hash和password_verify,我们在处理用户密码时能够确保更高的安全性和可靠性。这不仅提升了用户数据保护的水平,也使得代码更加健壮和易于维护。
参考:php.net/manual/zh/function.password-hash.php和php.net/manual/zh/function.password-verify.php
HashMap实现原理一步一步分析(1-put方法源码整体过程)
本文分享了HashMap内部的实现原理,重点解析了哈希(hash)、散列表(hash table)、哈希码(hashcode)以及hashCode()方法等基本概念。
哈希(hash)是将任意长度的输入通过散列算法转换为固定长度输出的过程,建立一一对应关系。常见算法包括MD5加密和ASCII码表。
散列表(hash table)是一种数据结构,通过关键码值映射到表中特定位置进行快速访问。
哈希码(hashcode)是散列表中对象的存储位置标识,用于查找效率。
Object类中的hashCode()方法用于获取对象的哈希码值,以在散列存储结构中确定对象存储地址。
在存储字母时,使用哈希码值对数组大小取模以适应存储范围,防止哈希碰撞。
HashMap在JDK1.7中使用数组+链表结构,而JDK1.8引入了红黑树以优化性能。
HashMap内部数据结构包含数组和Entry对象,数组用于存储Entry对象,Entry对象用于存储键值对。
在put方法中,首先判断数组是否为空并初始化,然后计算键的哈希码值对数组长度取模,用于定位存储位置。如果发生哈希碰撞,使用链表解决。
本文详细介绍了HashMap的存储机制,包括数组+链表的实现方式,以及如何处理哈希碰撞。后续文章将继续深入探讨HashMap的其他特性,如数组长度的优化、多线程环境下的性能优化和红黑树的引入。