JAVA后端知识点碎片化整理 基础篇(二) 集合

因为马上开始2019秋招、平时的学习比较琐碎、JAVA后端博大精深,想在暑假这段时间从头开始整理JAVA知识点查缺补漏,迎战2019秋招。主要参考(微信公众号)JAVA团长与(博客园)五月的仓颉的知识点复习线,对其列出的每一个的知识点再一次的咀嚼并谈谈自己的理解。(平时从这两位学到很多,也非常感谢身边同行的人)


1、List与Set区别

List、Set都继承与Collection接口。list特点是放入有序,元素可重复。set特点是放入无序,元素不可重复,若重复将覆盖。(set需要注意元素放入虽然是无序的但是元素在set中的位置是该元素的HashCode所决定的,其位置实际上是固定的,加入set的obecjt必须定义equal方法,list可以for和迭代来遍历,但set只能通过迭代)Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置的改变。List和数据类似,list可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置的改变。

2、Arraylist与linkedList的区别

Arraylist:优点Arraylist是基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作会比较高。(内存中连续存储)。缺点:由于地址连续,Arraylist要移动数据,所以插入和删除操作效率比较低。一旦我们实例化 ArrayList 无参数构造函数默认为数组初始化长度为 10②add 方法底层实现如果增加的元素个数超过了 10 个,那么 ArrayList 底层会新生成一个数组,长度为原数组的 1.5 倍+1,然后将原数组的内容复制到新数组当中,并且后续增加的内容都会放到新数组当中。当新数组无法容纳增加的元素时,重复该过程。是一旦数组超出长度,就开始扩容数组。扩容数组调用的方法 Arrays.copyOf(objArr, objArr.length + 1);

LinkedList:优点基于链表的数据结构,地址具有任意性,所以再开辟内存空间的时候不需要连续的地址,对于新增和删除操作,linkedlist比较占优势。缺点:因为LinkedList要移动指针,所以查询性能比较低。LinkedList 底层是一个链表,是由java实现的一个双向链表。

当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。

3、ArrayList与Vector区别

ArrayList与Vector都是通过数据实现的:主要由三个区别:1、Vector是多线程安全的,线程安全就是说多线程访问统一代码,不会产生不确定的结果。而ArrayList不是,Vector很多方法中都有synchronized进行修饰,导致多线程条件下vector更安全,但是效率上无法与arraylist相比。2、两个都是采用线性连续空间存储元素,但是当空间不足的时候两个类的增加方式是不同的。Vector默认增加原来空间的一倍,增长因子可以修改,而arraylist增加原来空间的50%。3、Vector是一种老的动态数组,是线程同步的,相率低下,一般不赞成使用。注意:Vector是线程同步的所以他也是线程安全的,而ArrayList是线程异步的是不安全的,如果不考虑到线程的安全因素,一般使用arraylist效率比较高。如果集合中的元素数目大于目前集合长度,在集合中使用数据量比较大的数据用vector有一定优势。

4、HashMap与HashTable的区别

扫描二维码关注公众号,回复: 2017208 查看本文章

1、hashMap去掉了HashTable的contains方法,当时加上了containsVaule与containsKey方法。2、hashtable是同步的,而hashMap是非同步的,效率上较hashTable要高。3、hashMap允许空值键,而hashTable不允许。

5、HashSet与HashMap的区别

set是线性结构,set中的值不能重复,hashset是set的hash实现,hashset中的值不能重复使用hashmap的key来实现的。map是键值对的映射,可是空键或空值,HashMap是map接口的hash实现,key的唯一性是通过key值得hash来唯一确定,value值则是链表结构。他们共同点都是hash算法实现其唯一性,他们都不能持有基本类型,只能持有对象。

6、HashMap的实现原理(数组+链表的实现)

数组:数组存储连续,占用内存严重,空间复杂比较大,但数组二分查找时间复杂度比较小。特点:寻址容易,插入删除困难。链表是存储区间的离散,占用内存比较宽松,但时间复杂度很大。特点:寻址困难,插入与删除容易。故希望综合两者的特性,做出一种寻址容易,插入与删除也容易的数据结构。于是hashMap 首先每一个元素都是链表的数组,当添加一个元素(key-value)时,首先计算key的hash值,一次缺点插入数组的位置,但是可能存在同一hash值,已确定插入数组的位置。但是可能存在同一hash元素就被放在数组的同一位置,但是形成了链表,同一各链表上的hash值是相同,所以说数组存放的是链表,同一个链表上的hash值是相同的,当链表长度太长时候,链表的缺点又会吧暴露出来,JDK1.8之后会再进行一次转换为红黑树,大大提高了查询的效率。其中有几个元素需要注意,加载因子?默认0.75,需要扩容时候用到。当hashmap一只不进行扩容,链表就会越来越长,这样查询效率就会很低(新版红黑树)扩容之后,之前的链表就会变成奇偶两个子链表分别挂在新链表数组的散列位置,这样来减少每个链表的长度,增加了查找效率。get(key)方法首先获取key的hash值,计算hash得在链表数组中的位置first与key是否相等,若不等则遍历后面的链表找到相同的key值返回对应value值。put(key,value)根据key计算hash值得到插入的数组索引i,如果数组中为空,直接根据节点添加。判断处理hash冲突的方式是链表还是红黑树,分别处理。扩容机制resize()构造hash表时,如果不指明初始大小,默认大小为16,如果Node【】数组中元素比例达到加载因子的阈值,重新调整hash的大小,变为原来的两倍(扩容非常耗时)。红黑树数据结构优化。hashmap处理碰撞增加了这种数据结构,当碰撞节点较少,采用链表存储,当较大时(>8)采用红黑树存储。问题分析:hashMap的性能带来灾难性的影响,如果多个hashcode的值落到一个桶内时候,这些值得存储到一个链表中,最坏的情况自然是,所有的key都映射到同一个桶内,这样hashmap就会退化成一个链表—查找时间从O(1)到O(n)。荣国红黑树,当一个数组内的记录过大的话(默认阈值是8)hashmap会动态的使用一个专门的treemap实现来替换。这样查询的0(logn)。使用hash值作为树的分支变量,如果两个hash值不等,较大的插入到右子树中,如果hash值相等,hashmap希望key值最好实现了Comparable接口的,这样他可以按照顺序来进行插入。

7、ConcurrentHashMap(自己对ConcurrentHashMap的说得并不好   附上连接https://www.jianshu.com/p/c0642afe03e0

原因:hashMap在高并发的环境下,执行put操作会导致HashMap的Entry链表形成的环形数据结构,从而导致Entry的next节点时钟不为空,因此产生死循环读取Entry。HashTable虽然线程安全的,但是效率低下,当一个线程访问Hashtable的同步方法时,其他线程如果也能访问HashTable同步方法,那么会进入阻塞或者轮训状态。   考虑上述两种情况,提出了新的ConcurrentHashMap数据结构采用锁分段技术,首先数据分为一段段去存储,然后每一段数据都会配置一个锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问。然后jdk1.8中的实现抛弃了segment所分段,利用CAS和Synchronize来保证,put操作采用CAS+synchronized实现并发插入或更新操作安全。对比一下新旧两种ConcurrentHashMap的实现:

JDK1.6分析:ConcurrentHashMap采用 分段锁的机制,实现并发的更新操作,底层由Segment数组和HashEntry数组组成。Segment继承ReentrantLock用来充当锁的角色,每个 Segment 对象守护每个散列映射表的若干个桶。HashEntry 用来封装映射表的键 / 值对;每个桶是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组,下面我们通过一个图来演示一下 ConcurrentHashMap 的结构:


JDK1.8分析 

改进一:取消segments字段,直接采用transient volatile HashEntry<K,V> table保存数据,采用table数组元素node作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。(这是由于JDK1.8Sychronize关键字有了新的定义,su)

改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。


猜你喜欢

转载自blog.csdn.net/weixin_39893439/article/details/80890542