JAVA集合框架(详解HashMap原理)

什么是集合框架?
用于存储数据的容器的统称,存在很多不同的容器,每一种容器根据底层储存数据的结构不一样,使用的场景也不一样.

集合框架图
在这里插入图片描述
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue(Queue是Java5新增的队列),再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

一,Collection接口
Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口,这个要牢记。

1.List接口
List集合代表一个有序、可重复集合,集合中每个元素都有其对应的顺序索引。List集合默认按照元素的添加顺序设置元素的索引,可以通过索引(类似数组的下标)来访问指定位置的集合元素。
List接口的集合主要有:ArrayList、LinkedList、Vector。

(1)ArrayList
-ArrayList:底层存储的数据结构为数组.(修改和查询较快,增加和删除速率较慢)
默认大小10,扩容系数为1.5
在这里插入图片描述
(2)LinkedList
底层存储的数据结构为双向链表.(修改和查询较慢,增加和删除较快)
没有长度大小,不用扩容
在这里插入图片描述
(3)Vector
底层存储的数据结构为数组,与ArrayList相似(底层数据是线程安全的,所以操作比ArrayList要慢)

2,Map接口
  Map接口采用键值对Map<K,V>的存储方式,保存具有映射关系的数据,因此,Map集合里保存两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value,key和value可以是任意引用类型的数据。key值不允许重复,可以为null。如果添加key-value对时Map中已经有重复的key,则新添加的value会覆盖该key原来对应的value。常用实现类有HashMap、LinkedHashMap、TreeMap等。
在这里插入图片描述
向一个map集合中添加元素

 
   Map<String, Integer> map = new HashMap<>();
    map.put("张三", 23);
    map.put("李四", 24);
    map.put("王五", 25);
    map.put("赵六", 26);
    

(1)hashmap
HashMap是基于哈希表实现的,用Entry数组来存储数据,而Entry中封装了key、value键值对
底层结构为数组链表结构,数组的大小必须是2的幂次方倍

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200325164008692.png
hashmap是数组链表,每一个数组(Entry)是一个链表。
结构:hash算法用于将key散列,经计算分散到数组槽中;而两个key算出了同样的值,即产生hash冲突时,就需要将槽中的单个节点升级成链表。由于get时需要对链表其进行遍历,链表越长检索效率越差。那么,计算出的key值落点越平均,hash冲突的可能性越小。
(原因:HashMap 容量为2次幂的原因,就是为了数据的的均匀分布,减少hash冲突)

由此引出问题:
hashmap是数组链表,每一个数组是一个链表,存储的时候,而两个key算出了同样的值,即产生hash冲突时,就产生链表可以一直往后面追加。

如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。HashMap中的链表出现越少,性能才会越好。

所以考虑到这点JDK8之后链表增加到一定长度变成红黑树。

什么是红黑树?
先要了解二叉树

二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。由于树的定义本身就是递归定义,因此採用递归的方法去实现树的三种遍历不仅easy理解并且代码非常简洁,而对于广度遍历来说,须要其他数据结构的支撑。比方堆了。所以。对于一段代码来说,可读性有时候要比代码本身的效率要重要的多。

在这里插入图片描述
有了二叉搜索树,当你要查找一个值,就不需要遍历整个序列或者说遍历整棵树了,可以根据当前遍历到的结点的值来确定搜索方向,这就好比你要去日本,假设你没有见过世界地图,你不知道该往哪个方向走,只能满地球找一遍才能保证一定能够到达日本;而如果你见过世界地图,你知道日本在中国的东边,你就不会往西走、往南走、往北走。这种思维在搜索中就把不必要的分枝剪掉可以提高搜索效率。在二叉搜索树中查找值,每次都会把搜索范围缩小,与二分搜索的思维类似。
在这里插入图片描述在这里插入图片描述

!. 有了二叉排序树就可以使插入、搜索效率大大提高了,为什么还要引入平衡二叉树?
二叉搜索树的结构与值的插入顺序有关,同一组数,若其元素的插入顺序不同,二叉搜索树的结构是千差万别的。举个例子,给出一组数[1,3,5,8,9,13]。
若按照[5,1,3,9,13,8]这样的顺序插入,其流程是这样的:

在这里插入图片描述在这里插入图片描述

!. 插入的序列越接近有序,生成的二叉搜索树就越像一个链表。时间复杂度就变大,性能降低
为了避免二叉搜索树变成“链表”,我们引入了平衡二叉树,即让树的结构看起来尽量“均匀”,左右子树的节点数尽量一样多。

在这里插入图片描述
平衡二叉树(Balanced Binary Tree)具有以下性质:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。那就是平衡二叉树的定义过于严格,导致每次插入或者删除一个元素之后,都要去维护二叉树整体的平衡,这样产生额外的代价又太大了。二叉搜索树可能退化成链表,那怎么办呢?这就要提到红黑树了。
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须除了满足二叉搜索树的性质外,还要满足下面的性质:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2)LinkedHashMap
LinkedHashMap是有序的,且默认为插入顺序。
将所有Entry节点链入一个双向链表双向链表的HashMap
在这里插入图片描述
(3)TreeMap
TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
TreeMap使用的存储结构就是红黑树。

额外知识补充:
HashTable是线程安全,HashMap是线程不安全的
原因:HashTable是读写方法都用了synchronized同步代码块修饰,所以读写都加了锁,并发性很差。但是代价非常高
HashTable访问效率低下的原因,就是因为所有的线程在竞争同一把锁.如果容器中有多把锁,不同的锁锁定不同的位置,这样线程间就不会存在锁的竞争,这样就可以有效的提高并发访问效率,这就是currentHashMap所使用的锁分段技术。

3,Set接口
Set是一种不包括重复元素的Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与List一样,它同样允许null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同。
Set就是用Map实现的,只用了Key,把Value设置成null
Set集合的数据库不能重复(== 或 eqauls)的元素

Set特点:无序,没有索引,元素不可重复
Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。

(1)hashSet:底层数据结构哈希表
 HashSet具有如下特点:
  ♦ 不能保证元素的顺序。
  ♦ HashSet不是线程同步的,如果多线程操作HashSet集合,则应通过代码来保证其同步。
  ♦ 集合元素值可以是null。
    
(HashSet有去重机制保证元素没有重复)就要说到Hashset的去重机制:
①在 HashSet集合中判断是否是同一个元素,首先判断对象的地址是否一样,如果地址不一样,说明是两个对象,直接存入,如果地址一样,则开启第二重去重机制判断对象中的值是否一致,如果一致,则说明是同一对象,不存.如果元素不相等,说明不是同一对象,则存入.

第一个机制,调用hashcod方法判断哈希值是否一致.
第二个机制,调用equals方法,判断值是否一致.

(2)TreeSet集合
1:底层数据结构是红黑树(是一个自平衡的二叉树)
2:保证元素的排序方式

(3)LinkedHashSet集合
底层数据结构由哈希表(是一个元素为链表的数组)和双向链表组成。

补充知识:

Object类中的equals方法和“==”是一样的,没有区别,即俩个对象的比较是比较他们的栈内存中存储的内存地址。

hashcode()是通过给对象生成hash值来判断对象是否相等一致。

因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠。
所以先用hashcod()比较,如果hashcod()都不相同那么一定不相同,
如果hashcod()相同,再用 equals()比较。先用hashcod()判断这样比较节约性能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值