Java中的集合类有哪些
List,Set,Queue,Stack,Map
Collection和Collections有什么区别?
Collection(可来客肾)提供了集合对象的基本操作接口。是list,set等的父接口
Collections(可来客肾思)是一个包装类,有各种有关集合操作的静态多态方法,不能被实例化,相当于是一个工具类。
?你能说出几种集合的排序方式?
比较常用的就是借助比较器进行排序,Collections.sort(可来客圣思.索特)用Lambda(烂达)表达式
第二种就是实现Comparable(抗怕了波)接口,重写compareTo(抗牌的To)方法,调用Collections.sort方法
遍历的同时删除一个List有几种方式
最简单的方式就是使用迭代器循环,在迭代器里面操作
通过Stream(思君母)的过滤方式,因为每次Stream处理完会生成一个新的Stream。
通过list本身的removeIf(瑞木)进行删除也可以
set是如何保证元素不重复
Set主要有两大分类,HashSet和TreeSet(去色特)
- TreeSet 是二叉树实现的,TreeSet中的数据是自动排好序的,不允许放入null值;底层基于TreeMap
- HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null
- 在HashSet底层是HashMap实现的,当向HashSet中添加元素的时候,首先计算元素的hashCode值,然后通过计算和按位与的方式计算出这个元素的存储位置,如果这个位置为空,就将元素添加进去;如果不为空,则用equals方法比较元素是否相等,相等就不添加,否则找一个空位添加
- TreeSet的底层是底层是TreeMap实现的,而TreeMap是基于红黑树实现的,红黑树是一种平衡二叉查找树,它能保证任何一个节点的左右子树的高度差不会超过较矮的那棵的一倍。
HashSet,TreeSet,LinkedHashSet,BitSet
- HashSet是功能最简单的Set,只提供去重的能力;
- TreeSet(去色特)提供了去重和排序的能力;
- LinkedHashSet(领特)不仅提供去重功能,而且还能记录插入顺序;
- BitSet不仅能提供去重能力,同时也能减少存储空间的浪费
ArrayList、LinkedList与Vector的区别
- ArrayList 底层是数组实现的.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问和操作,ArrayList本质上就是一个数组
- LinkedList 是一个双向链表,在添加和删除元素时具有比ArrayList更好的性能,但在获取原数弱于ArrayList。
- Vector(歪可特)是线程安全的,Vector类中的方法通过synchronized(肾可莱斯)修饰实现线程的同步,因为开启了线程同步导致性能较差。
ArrayList是如何扩容的
- 首先要声明一下ArrayList是基于数组实现的,因为数组创建的时候就一定有长度
- ArrayList的扩容首先新增元素判断是否超过数组的长度,如果超过 创建一个新的数组新的容量为老容量的1.5倍 并将老数组的元素复制到新数组中,扩容完成
ArrayList的缩容
首先ArrayList没有做缩容,因为频繁的操作数组的长度会影响性能,因此没有提供自动缩容,但是有提供一个方法手动调用就可以进行缩容。
为什么在JDK8中HashMap要转成红黑树
HashMap解决hash冲突是通过拉链法完成的,在JDK8之前,如果产生冲突,就会把新增的元素增加到当前所在的链表中
这样会导致,当某个节点冲突过多的时候,链表就会变得很长。
这个解决,可以使用二叉查找树,但是如果遇到极端情况,每次子节点都比父节点大或者小的时候,又会变成链表,查询复杂度会变成O(N),也就是说二叉查找数,存在一定的缺陷
在红黑树中,所有的叶子节点都是黑色的空节点,也就是叶子节点不存数据,红黑树不会追求绝对的平衡,它的插入最多旋转两次,删除最多旋转三次
HashMap为什么是链表长度达到8的时候转
官方认为因为哈希冲突造成的链表长度等于8的概率比较低,所以 8 这个数是经过数学推理的
HashMap为什么不在冲突的时候立刻转红黑树
因为红黑树的空间是普通链表节点空间的两倍,立刻转为红黑树后,太浪费空间;
红黑树虽然查询比链表快,但是插入比链表慢多了,每次插入都要旋转和变色,如果小于8就转为红黑树,时间和空间还不如链表
HashMap为什么长度为6的时候转回来
当红黑树节点数小于 6 时,又会把红黑树转换回链表,这个设计的主要原因是出于对于性能和空间的考虑
*HashMap、Hashtable和ConcurrentHashMap的区别
HashMap是线程不安全的
Hashtable 是线程安全的,它是直接使用synchronized来修饰核心方法,来加锁的,这也就相当于只用了一把锁,如果出现了多个线程同时读取值时候会照成堵塞等待。
ConcurrentHashMap是用的分段式,每个锁只是锁住了一段数据
ConcurrentHashMap(康扣瑞次)默认情况下将hash表分为16个分片,在加锁的时候,针对每个单独的分片进行加锁,其他分片不受影响
ConcurrentHashMap(康扣瑞次)采用了一种新的方式来实现线程安全,即使用了CAS+synchronized(肾可莱斯),这个实现被称为"分段锁"的变种,也被称为"锁分离",它将锁定粒度更细,把锁的粒度从整个Map降低到了单个桶
HashMap在get和put时经过哪些步骤
对于HashMap来说,底层是基于拉链式散列算法实现的即采用数组+链表/红黑树来解决hash冲突,数组是HashMap的主体,链表主要用来解决哈希冲突
- get方法
-
- 1、计算键的哈希值,并通过哈希值计算出在数组中的索引位置
- 2、如果位置上的元素为空,说明没有数据,直接返回null
- 3、如果该位置上的元素不为空,遍历该位置上的元素,如果找到了与当前键相等的键值对,那么返回该键值对的值,否则返回null
- put方法
-
- 1、put方法会计算键的哈希值,并通过哈希值计算出在数组中的索引位置
- 2、如果该位置上的元素为空,那么直接将键值对存储在该位置上
- 3、如果该位置上的元素不为空,那么遍历该位置上的元素,如果找到了与当前键相等的键值对,那么将该键值对的值更新为当前值,并返回旧值
- 4、如果该位置上的元素不为空,但没有与当前键相等的键值对,那么将键值对插入到链表或红黑树中
- 5、如果插入成功,返回被替换的值;如果插入失败,返回null
- 6、插入成功后,如果需要扩容,那么就进行一次扩容操作
HashMap是如何扩容的
首先我先说一下,hashmap的扩容触发条件
在hashmap中维护了一个threshold(非秀的)变量,用来保护阈值,这个阈值的数量是hashmap的槽位数乘已负载因子,负载因子的默认值值为0.75,也就是说hashmap的长度大于槽位数乘已负载因子就会触发扩容
扩容第一步,创建一个新的hash表,是旧的2倍
第二步:将旧的hash表移动到新的hash表中
HashMap的容量设置多少合适
阿里巴巴开发手册是有建议,创建hashmap的时候,指定初始值大小
阿里巴巴开发手册给的算法是(需要存储的元素个数/负载因子)+1
(待补充)Stream的并行流是如何实现的
Stream提高数据处理的效率,特别是在处理大型数据集时
HashMap的remove方法是如何实现的
remove方法会计算键的哈希值,并通过哈希值计算出在数组中的索引位置
如果该位置上的元素不为空,检查是否与当前键相等,如果相等,那么将该键值对删除,并返回该键值对的值
如果与当前键不相等,那么就需要在链表或红黑树中继续查找
遍历链表或者红黑树,查找与当前键相等的键值对,找到则将该键值对删除,并返回该键值对的值,否则返回null
ConcurrentHashMap是如何保证线程安全的
ConcurrentHashMap(康扣瑞次)在JDK 1.7中使用了分段锁技术
ConcurrentHashMap(康扣瑞次)在JDK 1.8中进行了改进,在添加元素时,如果某个段为空,那么使用CAS操作来添加新节点;如果段不为空,使用Synchronized(深哥莱斯)锁住当前段,再次尝试put,
这样可以避免
- 分段锁机制下的锁粒度太大,在高并发场景下,由于线程数量过多导致的锁竞争问题,提高了并发性能
Java 8中的Stream是用来干什么的
Stream(思君母)设计初衷是像SQL语句从数据库查询数据的直观方式
Stream优势是减少代码量,提高程序员的开发速度,并且提供了筛选,排序,聚合等操作
ConcurrentHashMap不允许null
ConcurrentHashMap(康扣瑞次)主要是考虑安全性
- ConcurrentHashMap(康扣瑞次)作者说在非并发的Map中,是可以容忍模糊性的,而在并发Map中是无法容忍的
ConcurrentHashMap(康扣瑞次)是为了解决并发而生的,在并发场景中是没办法检查key是否存在,因为在检测过程中可能会被其他线程所修改,避免出现结果并不可靠。