数组(牛肉丸)
如图:
数组初始化
- int [] a={1,2,3};
int a []={1,2,3}; //推荐使用 基本类型和引用类型存在栈内存中(jvm基础知识) - int [] a=new int[16]; new出来的存在堆内存中
知识解析
- 数组必须初始化,确定内存大小 (强行解释一波:新建一个数组,就像军队新建一个班,这个班有多少人是固定,比如一个班16人,那么必须是16人,大小已知,同样数组也是,内存是初始固定的,一上来就确定),一上来数组的内存大小就确定了,比如16条,如上图,数组是连续的,大小上来确定了,那么再插入或者删除一条就很麻烦,因为大小改变(数组缺点),同样因为是连续的,所以容易查询(优点)
- 数组,存储的数据是同一类型(再次强行解释一波:如同纲目分类一样,不同种类的不会分在一起吧,数组同样,相同类型的数据存在一起)
链表(撒尿虾)
知识解析
每个扣代表一个数据:
- 链表内存大小不用定义(强行解释一波,比如定义一个链表为海里的鱼类,它到底有多少种呢,我也不晓得,但是数组就不同了,定义一个数组为军队的连排班(如果非要抬杠还有加强连我也没得办法),他有多少人是固定的)
- 每个数据都两边都有扣(一个扣存数据,一个扣用来连接其它数据)
- 链表结构,看图就知道,想添加一个扣(数据),直接打开扣(寻找到相应的地址)扣上就ok了,即添加删除数据很方便,那么查询呢,扣扣相连,查询这个扣,就必须知道上一个扣的位置,所以查询很麻烦
总结一下
1. 数组(牛肉丸),内存固定,数据查询简单,插入难
2. 链表(撒尿虾),内存不定,数据插入简单,查询难
3. 那么问题来了,有什么结构即查询简单,又插入简单呢,HashMap(撒尿牛丸)隆重出场。
HashMap(撒尿牛丸)
结构图:
整体是一个数组,数组每个位置都是一个链表,链表的节点(存储单位)是一个entry,一个entry由hash,key,value,next四部分组成
特别说明一下,value值是一个object对象
举一个例子:
知识解析
知识解析:
HashMap 是key,value存储和获取值的。可以看到,第一次我用了key=老王,value=18时,他key的hash=104518,当我重新将key=老王的value值换成“隔壁时”,他的key的hash=104518,没有改变。
这是简单的的put存值,get取值,存取类似集合list,那么put工作原理具体是什么呢。
工作原理
首先,初始化HashMap,看上面的结构图,为什么我都是0-15,一共16个位置呢,这是有原因的,初始化的时候,容器默认的数组大小 initialCapacity (源码是这样定义的,源码就不看了,写这些都是为了给初学者看的,看源码的话,感觉有点烦)为 16,加载因子loadFactor 为0.75。容器的阈(yu)值为 initialCapacity * loadFactor,默认情况下阈值为 16 * 0.75 = 12
初始化时:
- 数组数组大小 initialCapacity为16,即16个位置
- 加载因子loadFactor 为0.75,(解释一下什么是加载因子:我现在有16个位置,不能每个位置都存储数据,需要留几个空位子,到底留几个呢,源码定义是要留25%,也就是说,16个位置最多用75%也就是12个)
- 阈(yu)值为 16 * 0.75 = 12(阈值的作用,看上图hs的size值变化,当我存了key=老李,value=开炮了,之后他的size从1变成了2,他们是两个entry,而当size>12时,也就是大于阈值时,怎么办?如果,size>12,数组长度先扩容为原先的二倍,)
一张图,看清第一次插入key='老王‘,value=’18’的具体步骤:
详细解释一下
插入:key=‘老王’,value=18时:
开始插入,数组不为空,因为我初始化了,初始化n=16,加载因子0.75,阈值16*0.75=12,'老王’的hash=1045418,那么(16-1)&hash=10,也就是说老王这个key存在10这个位置上,这个位置有值吗,当然没有,第一次啊,把key,value,next,hash封装成一个entry,放在10这个位置上,直接到下一步,entry个数就一个没有大于8,其实到这就结束了。
插入:key=‘老王’,value=‘隔壁’时:
同样走到key=10这个位置上,这个位置有值吗,有啊,key=‘老王’,value=18,一查key还TM的相等,怎么办呢,覆盖value。。
插入:key=‘老李’,value=‘开炮了’时:
'老李’的hash=1042285,(16-1)&hash=13,本来在13这个位置上,可以看到他插入成功后,老李+开炮了,放在了老王+隔壁前面,也就是说,新插入的放在前面。