java 基础复习--集合

1、集合的分类
(1、Collection: 可以理解为主要存放的单个对象,Collection 继承了Iterate 接口,Iterate 用于集合内迭代器抽象接口类,其子类均实现接口中方法。
(2、Map:可以理解为主要存储key-value 类型的对象。

2、Map
(1、关于hashmap 的一些说法(基于JDK1.8 )
a) hashmap 实际上是一个数组 + 链表 +红黑树的结合体
b) hashmap 的实例有两个参数影响其性能:初始容量 和 装添因子
c) hashmap 实现不同步,线程不安全。hashtable 是线程安全的。
d)hashmap 可以存null 键 和 null 值,不保证元素的顺序恒久不变,通过hashcode() 和 equals 方法保证键的唯一性。
(2、hash 碰撞解决的办法
a)、用开放定址法解决冲突的做法是:就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
插入时可能出现多次冲突的现象,容易产生堆积问题,不适于大规模的数据存储。
删除节点不能简单的将被删的节点置空,否则将截断在它之后填入散列表的同义词结点的查找路径。这是因为各种开放地址发中,空地址单元是查找失败的条件。因此在开发地址发处理冲突的散列表上执行删除操作,只能在被删节点上做删除标记。而不能正真删除结点。开放地址法的两种解决冲突的实现方法。

(1)线性探测再散列
di=1,2,3,…,m-1。
这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。
(2)平方探测再散列
这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。
b) 拉链法解决冲突的做法是:将所有键值的hash值相同的结点链接在一个单链表上。
拉链法的好处:处理冲突简单,且无堆积现象,即hash 值不同的键值不会发生冲突,因此平均查找长度比较短。
删除结点操作易于实现,只要简单地删除链表上响应的结点即可。
jdk1.8 版本后,Java 对 hashmap 版本做了改进,在链表长度大于8 (并且桶的长度大于64)的时候,在链表转为红黑树,以加快检索的速度。
c): 再哈希法: 同时构造多个不同的哈希函数,当一个冲突时使用下一个,直到不冲突为止。
d): 建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素一律填入溢出表之中。

3、为什么hashmap 容量一定为2 的幂次方呢?
答: hashmap 中的数据结构是数组+ 单链表 或者(红黑树)的结构,我们希望元素存放的更加均匀,这样查询效率是最高的,不需要遍历链表或者在红黑树中进行查找,而且空间利用率最大,时间复杂度最优。要达到分布均匀,最简单的办法就是采用取模运算,当容量为 2 的 n 次方时,h & (length - 1) == h % length, 它俩是等价不等效率的,显然位运算的效率非常高,这里的h 是二次哈希之后的值。length 为 2 的 n 次方时,length-1 的值所有二进制位全为1 ,在这种情况下index 值等于h值后几位,也就是最大程度的保哈希值,使得index 分布得更均匀,所以hashmap 的容量为2的n 次方时为了降低 hash碰撞的几率。

4、HashMap 基本方法详解(基于jdk1.8)
put():HashMap会对null值key进行特殊处理,总是放到table[0]位置。
put过程是先计算hash然后通过hash与table.length取摸计算index值,然后将key放到table[index]位置,当table[index]已存在其它元素时,会在table[index]位置形成一个链表,将新添加的元素放在table[index],原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,当元素数量达到临界值(capactiyfactor)时,则进行扩容,table数组长度变为table.length2。
jdk 1.7的扩容是插入之前之前判断,而jdk 1.8是插入之后再判断是否需要扩容;
在1.8中扩容时保持了原来链表中的顺序,避免出现死循环。
默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本。

jdk 1.8中的hashmap还引入了红黑树的数据结构,当桶的数量超过64且链表的长度超过8时进行树化,链表就会转成红黑树结构,提高查询效率。

get():同样当key为null时会进行特殊处理,在table[0]的链表上查找key为null的元素。get的过程是先计算hash然后通过hash与table.length取摸计算index值,然后遍历table[index]上的链表,直到找到key,然后返回。

remove方法和put get类似,计算hash,计算index,然后遍历查找,将找到的元素从table[index]链表移除。

resize方法在hashmap中并没有公开,这个方法实现了非常重要的hashmap扩容,具体过程为:先创建一个容量为 table.length2 的新table,修改临界值,然后把table里面元素计算hash值并使用hash与table.length2重新计算index放入到新的table里面。这里需要注意下是用每个元素的hash全部重新计算index,而不是简单的把原table对应index位置元素简单的移动到新table对应位置。

containsKey方法是先计算hash然后使用hash和table.length取摸得到index值,遍历table[index]元素查找是否包含key相同的值。

containsValue方法就比较粗暴了,就是直接遍历所有元素直到找到value,由此可见HashMap的containsValue方法本质上和普通数组和list的contains方法没什么区别,你别指望它会像containsKey那么高效。

hash函数(jdk 1.8):用了很多的异或,移位等运算,对key的hashcode进一步进行计算以及二进制位的调整来保证最终获取的存储位置尽量分布均匀。自己的高16位和低16位异或是为了混合原始哈希码的高位和低位,加大低位的随机性,同时低位也加入了高位的部分特征,高位的信息也保存下来。

clear方法非常简单,就是遍历table然后把每个位置置为null,同时修改元素个数为0。需要注意的是clear方法只会清除里面的元素,并不会重置capactiy。

猜你喜欢

转载自blog.csdn.net/weixin_43352448/article/details/87920010