HashMap:
HashMap是一个散列表,它存储的数据内容是键值对(Key-Value)的映射.
HashMap实现了Map接口, 根据键的HashCode值存储数据, 具有很快的访问速度, 最多允许一条记录的键为null值, 不支持线程同步.
HashMap是无序的,即 不会记录插入的顺序.
HashMap继承自AbstractMap,都实现了Map, cloneable(可克隆), Serializable(可序列化) 接口
Hash Table:
Hashtable是原始的java.util的一部分, 是一个Dictionary(排序接口)具体的实现.
然而, java 2 重构的Hashtable实现了Map接口, 因此, Hashtable现在集成到了集合框架中.它和HashMap类很相似, 但是它支持同步,HashMap不支持同步。
像HashMap一样, Hashtable在哈希表中存储键值对. 当使用一个哈希表, 要指定用作键的对象, 以及要连接到该键的值. 然后, 该键经过哈希处理, 所得到的散列码被用作存储在该表中值的索引.
两者的区别:
1.继承的父类不同
Hashtable继承于Dictionary类, 而HashMap继承于AbstractMap类. 但是两者都同时实现了Map, Cloneable(可复制), Serializable(可序列化)这三个接口.
2.内部实现使用的数组初始化和扩容方式不同
HashMap原始的数组长度为16, 而HashTable原始的数组长度为11.
HashMap要求底层数组的容量必须是2的整数次幂. HashTable不要求底层数组的容量必须是2的整数次幂.
HashMap的扩容机制是当前数组长度*2, 而HashTable扩容机制是当前长度*2-1,当数组容量达到当前数组长度的四分之三时,就会开始扩容.
创建的时候如果给定了容量的初始值,那么HashMap会将其扩充为2的幂次方大小,而HashTable会直接使用你给定的大小.
HashMap总是使用2的幂次作为哈希表的大小,而HashTable会尽量使用素数(质数),奇数.
3.底层不同
HashMap:
在JDK 8之前: 底层由数组+链表构成.
在JDK 8之后:底层由数组+链表+红黑树构成.
HashTable:
底层由数组+链表组成.
HashMap使用红黑树的时机
红黑树是一个趋于自平衡的二叉排序树(左右子树深度差的绝对值不超过1).当HashMap链表长度超过8的时候,底层会自动转换成红黑树.
4.添加Key Value的hash算法不同
HashMap添加元素时,是使用自定义的哈希算法,哈希算法是一种加密的摘要算法,哈希算法的加密是单向的,不可用密文解密得到明文;其作用是对任意的数据输入,计算得到一个固定长度的输出摘要;目的是为了校验数据是否被篡改,常用的哈希算法有4种
1.MD5算法
- 通过MessageDigest类的单例模式创建其对象
- 通过对象的update()方法更新原始数据
- 通过对象的digest()方法加密,返回值为字节数组
- 可通过StringBuilder类,将其转换成十六进制的字符串
- MD5加密后的字节数组为16字节,其转换成十六进制的字符串长度为32位
- 代码演示
public static void main(String[] args) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("MD"); md.update("helloWord".getBytes()); byte[] result = md.digest(); StringBuilder sb = new StringBuilder(); for (byte bite : result) { sb.append(String.format("%02x", bite)); } System.out.print(sb); }
2.SHA-1算法
通过MessageDigest类的单例模式创建其对象
通过其对象的update()方法更新原始数据
通过其对象的digest()方法加密,其返回值为字节数组
通过StringBuilder类,将字节数组转换成字符串
SHA-1算法加密后的字节数组为20字节,其转换成十六进制的字符串长度为40位
代码演示
public static void main(String[] args) throws NoSuchAlgorithmException { String password = "重生之我的程序人生"; MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(password.getBytes()); byte[] Array = digest.digest(); StringBuilder sb = new StringBuilder(); for (byte bite : Array) { sb.append(String.format("%02x", bite)); } System.out.println(sb); }
哈希算法加密中的MD5算法思路和SHA-1算法思路大同小异,两者除了对明文进行摘要加密还可以进行“加盐”;通过获取随机的数据,将此数据更新至明文中,一起加密;可在一定程度上防“彩虹表”的攻击。
3.HmacMD5算法
在Hmac算法加密时,分成密钥和加密两个部分:
生成密钥:
通过KeyGenerator类的单例模式创建其对象
其对象通过generateKey()方法生成Key
通过Key的getEncoded()方法生成密钥的字节数组
通过StringBuilder类,将密钥的字节数组转换成十六进行的字符串
加密:
1.通过Mac类的单例模式创建其对象
2.通过对象的Init()方法初始化密钥
3.通过对象的update()方法更新数据
4.通过对象的doFinal()方法加密,其返回值为加密的字节数组
5.通过StringBuilder类,将加密的字节数组转换成十六进制的字符串
生成的密钥为字节数为64字节,其加密后的字节数组为16字节
代码演示
String passworf = "重生之我的程序人生"; // 生成密钥 KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); SecretKey key = keyGen.generateKey(); byte[] keyByteArray = key.getEncoded(); // 加密 Mac mac = Mac.getInstance("HmacMD5"); mac.init(key); mac.update(passworf.getBytes()); byte[] resultArray = mac.doFinal(); StringBuilder sb = new StringBuilder(); for(byte bite:resultArray) { sb.append(String.format("%02x", bite)); } System.out.println("加密的内容:"+sb);
4.RipeMD160算法
此算法的加密基于BouncyCastle提供的第三方库
注册BouncyCastle提供的注册类对象BouncyCastleProvider
通过MessageDigest类的单例模式创建其对象
通过其对象的update()方法,更新原始数据
通过其对象的digest()方法,进行加密,其返回值为字节数组
可通过BigInteger()方法,将字节数组转换成十六进制的字符串
加密后字节数组为20字节,其转换成十六进制为40位
代码演示
// 添加第三方库的对象 Security.addProvider(new BouncyCastleProvider()); MessageDigest digest = MessageDigest.getInstance("RipeMD160"); digest.update("helloworld".getBytes()); byte[] result = digest.digest(); String hex = new BigInteger(1, result).toString(16); System.out.println("字符串的内容" + hex);
哈希算法进行加密,都是摘要加密;加密后不可逆,也就是不可通过解密得到明文。
2.而HashTable是直接采用key的hashCode()
5.线程安全性
HashMap是线程不安全的,多线程的情况之下会造成并发的冲突,但是单线程的情况之下运行的效率比较高.
HashTable是线程安全的,很多方法都是用synchronized修饰,但因为加了锁导致并发的效率比较低,单线程环境效率也很低.
另外在HashMap结构中,是允许保存null值的,Entry.Key和Entry.Value均可以为null.但是HashTable中是不允许保存null的.
6.实现方式不同
HashMap: 基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,我们可以调优初始容量和负载因子。
HashMap(): 构建一个空的哈希映像
HashMap(Map m): 构建一个哈希映像,并且添加映像m的所有映射
HashMap(int initialCapacity): 构建一个拥有特定容量的空的哈希映像
HashMap(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空的哈希映像
TreeMap: 基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
TreeMap():构建一个空的映像树
TreeMap(Map m): 构建一个映像树,并且添加映像m中所有元素
TreeMap(Comparator c): 构建一个映像树,并且使用特定的比较器对关键字进行排序
TreeMap(SortedMap s): 构建一个映像树,添加映像树s中所有映射,并且使用与有序映像s相同的比较器排序
我们如何决定使用HashMap还是TreeMap呢?
HashMap<K,V>的Key值实现散列HashCode(),分布是散列的,均匀的,并不支持排序,数据结构主要是数组,链表,红黑树. 适用于在Map中进行插入,删除和定位元素的操作.
TreeMap<K,V>的Key值是要求实现java.lang.Comparable(可比较),所以TreeMap迭代的时候默认的是Key值升序排序的,TreeMap的实现是基于红黑树结构.适用于按自然顺序或者自定义顺序来遍历Key值.
因为HashMap中的元素的排列顺序是不固定的,所以如果你需要得到一个有序的结果则建议使用TreeMap.
但是HashMap有更好的性能,所以大多不需要排序的时候,则建议使用HashMap。