HashMap学习

HashMap学习

  • 线性表
  • HashMap
  • SparseArray
  • ArrayMap

线性表

线性表分为顺序表和链表,典型代表就是ArrayList和LinkeList
ArrayList是顺序表,底层数组实现,内存地址连续,有下标,所以查找快,但是增删相对慢,因为数组的位移每次都要移动后面所有的元素,底层是调用System.arraycopy进行元素的移动。

LinkeList是双向链表,内存地址不连续,查找、修改慢,但是增删不需要元素那么复杂的移位,所以增删快。
HashMap学习_第1张图片

HashMap

线性表要不增删快、要不查找慢,有没有一种数据结构兼顾两者之间的平衡呢?那就是HashMap
HashMap:1.7之前 24 之前: 数组+ 链表
HashMap:1.8 之后: 数组+ 链表 + 红黑树

transient HashMapEntry[] table = (HashMapEntry[]) EMPTY_TABLE
HashMap学习_第2张图片
1.8的时候当同一个index下的元素=8个会转为红黑树,当红黑书的数量=6的时候会转为链表。

看下源码中put函数:

HashMap学习_第3张图片
key不等于空的时候获取它的hashcode值,是一个int类型。
key:从Object --》int
要将key转换后的int值放到table[]里面去,
将得到的int对lenght进行求摸运算就能得到0~(lenght-1) 取模 === hash & (length -1)
而:hashCode % length) == h & (length-1) 而源码正是是用&运算来进行计算的
所有的CPU都是进行位运算的:CPU: 位运算 位移
包括我们平常写的+ 或者-,最终都转成 &或者!等位运算。
所以源码正是使用了 h & (length-1)

理解了key转换成hashcode之后如何存储的问题,那么这种情况下会出现多对一的情况,也就是&运算后的index值相同,我们称之为哈希碰撞或者哈希冲突。

hashmap是怎么操作的:头插法
不管对应的table[]下的index对象,也就是链表是否存在元素,都采用头插法,将元素添加到第一个。
这是put的情况,那么问题来了,get的时候是怎么获取的呢?

根据key的hashcode找到对应的下标的table,然后轮询查找对应的元素。
HashMap学习_第4张图片
找到对应元素的key与get的key一致的就是要找的对象。

由于key是唯一的,所以put的时候肯定也要轮询查看是否当前对应index下的table是否包含对应元素。

既然是一个容器,那么也就存在扩容的问题,hashmap是什么情况下进行扩容呢?

创建的时候table[]的长度必须是2的次幂,默认是16开始,如果你创建的时候传个15,底层也会默认给你变成16,大于16则变成32,以此类推。
为什么一定是2的次幂

因为二进制中进行&运算的时候,二进制码都是1。

尽量减少hash碰撞,而且充分占据每个数组的位置,必须要保证(数组大小-1)的二进制全是1,例如(16-1)的二进制是1111。
&运算,二进制码如果是0就没有意义,肯定都是0,只有1才有变化,尽量让每个table的index都存储到元素,减少hash冲突。

大概的结构我们都了解了,那么可能存在某个index下的table存储了很多个元素,而某些table却很少,当链表的长度变长了之后,我们进行查找和修改就越来越慢了。

所以综合考虑,当table[]中存储元素的table达到0.75的阈值之后,再存储put的时候就会进行扩容x2
这样做的话肯定会浪费一些空间,以空间换时间。效率问题

SparseArray

根据hashmap的情况,android又专门为我们开发了一个容器。
HashMap学习_第5张图片
明显可以看到它是双数组结构,key是int,values是Object。

HashMap学习_第6张图片
key int类型 key是按照顺序大小摆放,插入中间的话后面的位置全部arraycopy key放的位置是2
values放的位置也是2
既然是顺序大小摆放,那么查找就可以使用二分查找法。
remove的时候把对应的位置的值标志位delee,不用元素位移。

速度是越用越快:数据约大优势越大,1000没多大差距,10000差距明显。
缺点:key只能是int

ArrayMap

hashmap + SparseArray = ArrayMap
key可以是泛型,同样转为hashcode变成int,双数组模式。

既然转为hashcode,那么就存在同样的hashcode,如果存在了怎么办?
如果存在hashcode冲突,则往后面一位放
取的时候则判断这个值key是否相同,不相等则往后再取。

最后总结一下:hashmap < SparseArray < ArrayMap
当前在项目中最常用的还是hashmap,有多方面的原因。

首先我们android平时用的时候数据量都不大,使用哪一种真的关系也不大,而且hashmap毕竟是一开始就有的,大家都使用熟悉了。

你可能感兴趣的:(android筑基,数据结构,链表)